Strange Ghost Object appearing - ruby-on-rails

I currently have 3 Models, UserModel (Devise), an ArticleModel and an CommentModel.
I work with mongoid.
class Comment
include Mongoid::Document
field :body, type: String
field :created_at, type: Time, default: DateTime.now
belongs_to :article
belongs_to :user
end
class Article
include Mongoid::Document
field :title, type: String
field :body, type: String
field :created_at, type: DateTime, default: DateTime.now
has_many :comments
belongs_to :user
end
class User
include Mongoid::Document
has_many :articles
has_many :comments
end
my article/show.html.haml
.comments-form
.row
.span12
= form_for([#article, #article.comments.build]) do |f|
= f.text_area :body, :class => "span12", :rows => "3", :placeholder => "Your comment here..."
= f.submit nil, :class => "btn"
.comments
- #article.comments.each do |comment|
.comment{:id => "#{comment.id}"}
.row
.span2
= image_tag("260x180.gif", :class => "thumbnail")
.span10
= comment.body
my comments_controller
class CommentsController < ApplicationController
def new
#article = Article.find(params[:article_id])
#comment = #article.comments.create(params[:comment])
#comment.user = current_user
#comment.save
redirect_to article_path(#article, notice: "Comment posted.")
end
end
now in my articles/show appears an comment with an id but completely empty and i just can't figure out where this object comes from...

Your problem is with this line:
= form_for([#article, #article.comments.build]) do |f|
When you call #article.comments.build, you're buildling a new comment and adding it to #article.comments. Further down when you iterate over your comments, you're seeing the empty one you just created.
Instead of [#article, #article.comments.build], use [#article, Comment.new].

Related

Request parameters not saving on model_params on n:n relationship with check_box_tag

I am using Rails 5.1 and im having some issues saving params on an n:n relationship.
I have three models:
class Course < ApplicationRecord
belongs_to :studio
has_many :reviews, dependent: :destroy
has_many :has_category
has_many :categories, through: :has_category
validates :name, presence: true
end
class Category < ApplicationRecord
has_many :has_category
has_many :courses, through: :has_category
end
class HasCategory < ApplicationRecord
belongs_to :category
belongs_to :course
end
and a simple form to create a new course with different categories using check_box_tag (not sure if using it correctly though)
<%= simple_form_for [#studio, #course] do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<% #categories.each do |category| %>
<%= check_box_tag "course[category_ids][]", category.id, true %>
<%= category.name%>
<% end %>
<%= f.button :submit %>
<% end %>
And all is permitted and created on the courses controller:
def new
#studio = Studio.find(params[:studio_id])
#course = Course.new
#course.studio = #studio
#categories = Category.all
end
def create
#studio = Studio.find(params[:studio_id])
#course = Course.new(course_params)
#course.studio = #studio
#categories = params[:category_ids]
if #course.save
redirect_to course_path(#course)
else
render :new
end
end
def course_params
params.require(:course).permit(:studio_id, :name, :description, :category_ids)
end
With better_errors i know the categories are being requested, here the request info:
"course"=>{"name"=>"Course test", "description"=>"testing", "category_ids"=>["2", "3"]}, "commit"=>"Create Course", "controller"=>"courses", "action"=>"create", "studio_id"=>"16"}
but the categories are not saved on course_params, HasCategory instance or on the Course, i´ve tried with #course.categories = params[:category_ids] and other solutions without success.
How do i save the categories to the courses?
Try the following
Change the strong parameter with category_ids: []
def course_params
params.require(:course).permit(:studio_id, :name, :description, category_ids: [])
end
Comment out this line #categories = params[:category_ids]
Hope it helps

Multi Select: attaches an additional empty id

when I want to create a new book, I need to attach it to categories. but when I try to save a new book, I get the error: "Could not find the category with < id "=". > I do not know why, but one more id attach besides those I have already attached. And this id is empty ("").
it looks like this ("book" => {"name" => "12", "category_ids" => ["", "2", "7"], ....). Help me please.
my bookcontroler:
def new
#book = Book.new(book_params)
end
def create
#book = Book.new(book_params)
#category = Category.find(id: params[:ids])
BookCategory.create!(book: #book, category: #category)
#book.save
end
private
def book_params
params.require(:book).permit(:name, :author, :image, :description)
end
my form:
<%= form_for (#book) do |f| %>
<%= f.collection_select :category_ids, Category.order(:name), :id, :name, {},{ multiple: :true } %><br>
<% end %>
my error: enter image description here
Provided you have setup the associations correctly:
class Book < ApplicationRecord
has_many :book_categories
has_many :categories, through: :book_categories
end
class Category < ApplicationRecord
has_many :book_categories
has_many :books, through: :book_categories
end
class BookCategory < ApplicationRecord
belongs_to :book
belongs_to :category
end
For every has_many association ActiveRecord creates an _ids setter and getter:
#book = Book.find(1)
#book.category_ids
# => [1, 2, 3]
#book.category_ids = [2,4] # will add 4 and remove 1 and 3.
So you don't need to manually setup the associations, just pass the category_ids param:
def new
#book = Book.new # don't assign params in the new method!
end
def create
#book = Book.new(book_params)
if #book.save
redirect_to #book
else
render :new
end
end
private
def book_params
# category_ids: [] allows an array of scalar values
params.require(:book)
.permit(:name, :author, :image, :description, category_ids: [])
end
Your code won't work since you are using .find with an array param that can be nil or empty.
Also the options argument for collection_select is optional:
<%= form_for (#book) do |f| %>
<%= f.collection_select :category_ids, Category.order(:name), :id, :name, { multiple: :true } %><br>
<% end %>

Rails nested fields creation in loop

I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.

simple_form association doesn't save belongs_to id

I have a recipient and category model. It's a simple association of 1 category has many recipients. When I try to update the recipient form and assign a category, it won't save to the record. If I use the console and update a record manually, e.g. Recipient.update(9, category_id: 13), I see the correct category assigned to the recipient but when I try to edit/update the record, it won't save to the new chosen category.
Here is my recipient model
class Recipient < ActiveRecord::Base
belongs_to :category
accepts_nested_attributes_for :category
end
Here is my category model
class Category < ActiveRecord::Base
has_many :recipients
validates :category, presence: true
default_scope { order('category')}
end
here is the recipient controller
class RecipientsController < ApplicationController
def index
#recipients = Recipient.order(:recipient_name).page(params[:page])
end
def new
#recipient = Recipient.new
end
def show
#recipient = Recipient.find(params[:id])
end
def create
#recipient = Recipient.new(recipient_params)
if #recipient.save
redirect_to recipients_path
else
render :new
end
end
def edit
#recipient = Recipient.find(params[:id])
end
def update
#recipient = Recipient.find(params[:id])
recipient_params = params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, category_attributes: [:category, :id])
#recipient.update_attributes(recipient_params)
redirect_to recipient_path(id: #recipient.id)
end
def destroy
#recipient = Recipient.find(params[:id])
#recipient.destroy
redirect_to recipients_path
end
private
def recipient_params
params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, product_attributes: [:product_name, recipient_products: [:recipient_id, :product_id]], channel_attributes: [:channel_name, recipient_channels: [:recipient_id, :channel_id]], category_attributes: [:id, :category])
end
end
here is the edit view
<div class="row">
<div class="col-sm-6">
<h2>Edit <%= #recipient.recipient_name %></h2>
<%= simple_form_for #recipient do |form| %>
<%= form.error_notification %>
<%= form.input :recipient_name, placeholder: 'Recipient', label: 'Recipient Name' %>
<%= form.input :alternate_name, placeholder: 'Alternate Name' %>
<%= form.association :category, label_method: :category, value_method: :id %>
<%= form.input :description, placeholder: 'Description'%>
<%= form.input :city, placeholder: 'City'%>
<%= form.input :state, placeholder: 'State' %>
<%= form.input :country, as: :country, priority: ['US', 'CA'] %>
<%= form.button :submit, 'Update Recipient', {:class=>"btn btn-secondary"} %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
</div>
</div>
and here is my routes.rb file
Rails.application.routes.draw do
root to: 'home#index'
resources :media_points
resources :products
resources :channels
resources :recipients
resources :media_point_products
resources :distributions
resources :categories do
resources :recipients
end
get '/listing' => "listing#index"
devise_for :admins
devise_for :users
resources :users
end
I wrote it as an answer since the format is a pain in comments:
Change your recipient_params private method from:
category_attributes: [:category, :id]
to
category_id: param_that_has_category_id_here
Also, you have two routes for recipients and it is going to utilize the first matching route which is not the nested recipients in categories route you have further down. If the first change in your private method didn't fix it I would specify the nested situation in simpleform doing as follows since you are probably looking to use the nested route:
simple_form_for [#category, #recipient], url: 'nested_route_path_here' do |f|
Just add #category = Category.new to your new action in the recipients controller and then in your create action you'll have the category param sent via params[:category_id]

Rails 3.1: Nested attributes won't save through the form

I suspect this might be a very simple mistake but I've spent 3 hours looking for it so I thought I might ask for some help from the community.
I'm running through Ryan Bates' excellent screencasts on Nested Models Forms and trying to apply them to my own project. The problem is the nested attribute doesn't seem to save using the form. I can get it to save through the console but it only shows up as empty brackets when going through the form.
Here's the relevant code:
The form view (using haml)
= form_for(#article) do |f|
- if #article.errors.any?
#error_explanation
%h2
= pluralize(#article.errors.count, "error")
prohibited this article from being saved:
%ul
- #article.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :title
%br/
= f.text_field :title
.field
= f.label :intro
%br/
= f.text_area :intro
= f.fields_for :subsections do |builder|
= render 'subsections_fields', :f => builder
.field
= f.label :published_at
%br/
= f.text_field :published_at
.actions
= submit_or_cancel(f)
subsection_fields form view
= f.label :header
%br/
= f.text_field :header
= f.label :order_id
= f.number_field :order_id
%br/
= f.label :body
%br/
= f.text_area :body
%br/
= f.check_box :_destroy
= f.label :_destroy, "Remove Subsection"
%br/
Controller
class ArticlesController < ApplicationController
def new
#article = Article.new
3.times { #article.subsections.build }
end
def create
#article = Article.new(params[:article])
if #article.save
flash[:notice] = "Successfully created article."
redirect_to #article
else
render :action => 'new'
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
flash[:notice] = "Successfully updated article."
redirect_to #survey
else
render :action => 'edit'
end
end
def destroy
Article.find(params[:id]).destroy
flash[:notice] = "Succesfully destroy article."
redirect_to articles_url
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
end
And the models
class Article < ActiveRecord::Base
attr_accessible :title, :intro
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end
class Subsection < ActiveRecord::Base
attr_accessible :header, :body, :order_id
belongs_to :article
validates :header, :presence => true
validates :body, :presence => true
end
Any help figuring this out is much appreciated.
I'm not quite sure, but try it with attr_accessible :article_id as well in your Subsection model?
Adding "attr_accessible" to a model changes the way mass assignment works in rails.
If you remove the "attr_accessible" lines in your models then all your code will work perfectly as it is.
The class method "accepts_nested_attributes_for" adds a "subsections_attributes=(value)" method to your model.
The second you add "attr_accessible" to a model you now are forced into adding extra "attr_accessible" entries for each field that you want to assign via mass assignment. i.e. when you use Article.new(params[:article]).
I hope that was clear.
I figured out the answer to this one from another question.
The answer was to set my subsections_attributes as an attr_accessible, so the above Article model should look like this (I also added published_at as an attr_accessible):
class Article < ActiveRecord::Base
attr_accessible :title, :intro, :subsections_attributes, :published_at
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end

Resources