rails undefined method `each' for #<Field - ruby-on-rails

I have a form that allows user to register for a tournament. In the process of building the registration form I hae dynamic nested fields with so far field_type of basic. When I load the participants new form I am trying to load all the fields from the Fields table with field_type of basic. It will find them and if I just try <%= #basic.name %> from the new form it will give the name of the last field in the database with that field_type, but if I try:
<% #basic.each do |b| %>
<%= b.name %>
<% end
I get the error undefined method `each' for #<Field.
Here is the new action from the participants_controller:
def new
#participant = #event.participants.new
#user = User.find(current_user.id)
#children = #user.children
#basic = Field.find_by(event_id: #event.id, field_type: 'basic')
end
Fields belong to events but do I have to connect them to participants to make this work?
Thanks

find_by only returns a single record (or nil if the criteria aren't matched) use where to return a collection.
#basic = Field.where(event_id: #event.id, field_type: 'basic')
However assuming you have the association has_many :fields defined in Event you could also use:
#basic = #event.fields.where(field_type: 'basic')
And if you have the scope :basic, -> { where(field_type: 'basic') } defined in Field you can further simplify to:
#basic = #event.fields.basic

Related

Saving arrays in Rails 4.2.3

I am having some trouble saving arrays in Rails.
Rails version: 4.2.3 | Ruby version: 2.2.1 | DB: PostgreSQL
In my view, I have a collection of check boxes that shows the conferences that my member attended.
<%= f.fields_for :conferences_member do |conference| %>
<%= collection_check_boxes(:conferences_member, :conference_id, #all_conferences, :id, :name)%>
<% end %>
I put a break point (binding.pry) after the create action in my MembersController, and surprisingly, it shows the selected check boxes:
Processing by Database::MembersController#create as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>"XYZ==",
[...] "conferences_member"=> {"conference_id"=>["3", "5", ""]}, [...]
Now, if I go to rails c, and type ConferencesMember.last to check what was saved, I get:
pry(main)> ConferencesMember.last
ConferencesMember Load (0.5ms) SELECT "conferences_members".* FROM
"conferences_members" ORDER BY "conferences_members"."id" DESC LIMIT 1
=> nil
These are my associations:
#=> member.rb
has_one :conferences_member
accepts_nested_attributes_for :conferences_member, allow_destroy: true, reject_if: :all_blank
#=> conferences_member.rb
serialize :conference_id, Array
belongs_to :member
#=> members_controller.rb
params.require(:member).permit( [...]
:conference_member_attributes => [:id, :member_id, :conference_id => []],
[...])
I want to thank you in advance. I've tried almost everything here on StackOverflow, but I don't see my error.
Thank you again.
EDIT:
More of my MembersController:
def new
#member = Member.new
#member.build_conferences_member
end
def create
#member = Member.new(member_params)
binding.pry
end
The log doesn't show any error, it just shows that conferences were not saved at all.
First, your field needs to be renamed to nest the :conference_id in :conferences_member_attributes (not in :conferences_member as you do now). Take advantage of the form object yielded by fields_for:
<%= f.fields_for :conferences_member do |conference| %>
<%= conference.collection_check_boxes :conference_id, #all_conferences, :id, :name %>
<% end %>
You also need to actually save the record in the create action: Member.new builds the record but does not save it. Typically, the create action branches based on whether the record saved or did not (due to validations). So you might rewrite this method like so:
def create
#member = Member.new(member_params)
# when #member.save returns true, it saved to the db successfully
if #member.save
redirect_to members_path, notice: "Member #{#member.id} saved!"
# otherwise, it didn't save because of a validation error, so we render the error
# to the user and give them a chance to fix it
else
flash[:error] = "Member didn't save: #{#member.errors.full_messages.to_sentence}"
render :new
end
end
Lastly, to make sure your data gets through your strong parameters, check your logs for any messages that parameters were filtered out. The messages look like:
Unpermitted parameters: your_favorite_attribute

how to mark as checked for associated values in Rails 4 checkbox with many to many association

i am creating a user ACL i have 2 models one is users_group and another is access_sections.
Now let's supouse I have 2 access_sections (pages, users)
I wanted to associate both access_sections with users_group,
Now here what i have already did
I created many to many association, added new join table access_sections_user_groups
Whenever i add new user group I pass instance variable to get all available access_sections the I show them as a checkbox in a new user group form, once i check any of access section I loop through in array and insert all elements in 'table access_sections_user_groups' with users_group id and access_sections id everything is working now i wanted to do that when I edit usergroup my checkbox should be checked if the specific access_sections is associated with user_group
user_group_controller.rb
def create
#user_group = UserGroup.new(group_params)
if #user_group.save
flash[:notice] = "User group added !"
flash[:type] = "success"
if params[:user_group][:access_sections].present?
params[:user_group][:access_sections].each do |f|
UserGroup.find(#user_group.id).access_sections << AccessSection.find(f)
end
end
redirect_to(:action => "index")
else
flash[:notice] = "error while adding new group!"
flash[:type] = "danger"
render("add_new")
end
end
user_group/_form.html.erb
<%= f.label("Add section to Access Control ") %>
<% #acl_sections.each do |k| %>
<%= f.check_box(:access_sections, { :multiple => true }, k.id, nil) %>
<% end %>
Should work
f.check_box(:access_section_ids, { :multiple => true }, k.id, nil)
Hence you need to update your model like this way:
def create
if #user_group.create(user_group_params)
# ...
end
end
def user_group_params
params.require(:user_group).permit(access_section_ids: [])
end

Form for taggable post model

I'm working in a form for post than can have tags. The relationship is a classic has_and_belongs_to_many between Post and Tag.
Problem is I can't initialize a post with an array of tag ids, like this:
Post.new(tags: [1, 2, 3, 4]) # won't wotk. ActiveRecord expects Tag instances
So my current form is like this:
= form_for #post do |f|
= f.text_field :title, placeholder: 'title...'
= f.text_area :body
= fields_for :'post[tags]' do |ft| # hacky. using #post.tags raised 'undefined `model name` for `#post.tags`'
- Post.tags.each do |tag| # I defined Post::tags since I can't Acces Tag class here
= ft.check_box tag.id
= tag.name
= f.submit 'Save'
This form forces me to hack a little in either the controller, but seems like a bad practice. I also thought I could override ActiveRecord::Base initializators to allow ids so my first example works, but surely I'm missing something here.
Try setting tags_singular_ids to your ids. You can check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many for more of the methods that has_and_belongs_to_many provides.
Easy and bit hacky solution:
# defined in my posts controller
def post_params
p = params.require(:post).merge(user: current_user)
p[:tags] = p[:tags].map do |id, value|
value == '1' ? Tag.find(id) : nil
end.compact!
p
end

Ruby On Rails multi selector with many to many relation

I have two models
Project which has has_and_belongs_to_many :users
and
Users which has has_and_belongs_to_many :projects
in project view I have a form that has a selector where I want to try and assign multiple users to a project.
<%= select_tag("project[users]", options_for_select(#users, #project_users), {:multiple=>true, :size=>6} )%>
in my project controller the variables that get used in the select are
#project_users = #project.users.collect { |user| user.id}
#users = User.all.collect { |user| ["#{user.first_name} #{user.last_name}", user.id] }
which all out puts
<select id="project_users" multiple="multiple" name="project[users][]" size="6">
<option value="#<User:0x007f567cb7f078>">User1</option>
<option value="#<User:0x007f567cb7e9c0>">User2</option>
</select>
The problem is that this is not the equivalent to
#some_project << [User(#), User(#)]
("note User(#) represent class instance")
instead its the equivalent to
#some_project << ["1", "2"]
The problem is the user instance gets converted to string but not back into the instance again.
Which does not work and will throw an error as
ActiveRecord::AssociationTypeMismatch in ProjectsController#update
User(#70004716784160) expected, got String(#4266680)
How can I make this work correctly?
In your User model:
def full_name
[first_name, last_name].join(" ")
end
In your controller
#users = User.all
In your view:
<%= select_tag('project[user_ids]', options_from_collection_for_select(#users, 'id', 'full_name'), { :multiple => true, :size => 6 }) %>
Then you can just use
#project.user_ids = params[:project][:user_ids]
or
#project.update_attributes(params[:project])
for assignment.
You can't send the instance. It can change between the render of the form and the submit. The conversion to string is a one way operation, as it refers to the object's unique id (like memory address), not it's properties or database identifier! As it is not part of the ActiveRecord but the ruby's object base. When the data of the form is returned, the instance is not in the memory, so you can't convert it back, even if it would be possible otherwise.
Stick to the plain old way:
#project_users = #project.users.collect { |user| user.id}
#users = User.all.collect { |user| ["#{user.first_name} #{user.last_name}", user.id] }
And when the data is submitted:
#users = params[:project][:users].map{|a| User.find(a) }

How to send user.id with :user in params even though user.name is what is in the form drop down?

Currently, I have an action in my customers controller generating an array of names, #username_array, of all objects of class User with which to populate a drop down menu in a form that creates a new object of class Customer. The form element looks like this right now:
<%= f.select :user_id, #username_array %>
What I'd really like is for the id of the user to be sent into params[:customer][:user_id] instead of the name of that user that is chosen in the drop down. So in my create action in my customers controller I have the following code:
#customer = Customer.new(params[:customer])
#user = User.find_by_name(params[:customer][:user_id]) # where :user_id is actually currently the name selected from the drop down
#customer.user_id = #user.id
Is there an easier way of doing this?
Change your #username_array to include both the name and the id of the user:
Instead of:
["Bob","Sally","Dave"]
Make it:
[["Bob", 1],["Sally",2],["Dave",3]]
This could be accomplished by something like this:
#username_array = User.all.map {|user| [user.name, user.id]}
Then, f.select will display the name in the dropdown, but the actual value passed in through params[:customer][:user_id] will be the id of the user, which is what you want. With this in place, the following is all you need in the controller action code:
#customer = Customer.new(params[:customer])
You won't have to look up the user by name, the params hash will already have the correct id value.
Note that instead of making #username_array an instance variable you could just create a utility method in the helper for this controller, or the application helper:
def user_select
User.all.map {|user| [user.name, user.id]}
end
Then, in your view:
<%= f.select :user_id, user_select %>
If you put this in your application helper, you can use it everywhere and only have the code in one place (DRY).
you can do
#user = User.find_by_name(params[:customer][:user_id])
#user.customers.new(params[:customer])
or
#user = User.find_by_name(params[:customer][:user_id])
#customer = #user.customers.create(params[:customer])
but to do that you must have the relation (has_many, belongs_to,...)
or
Customer.new(params[:customer], :user_id => params[:customer][:user_id])
or
f.collection_select :user_id, #username_array, :id, :name

Resources