Rails, fields_for, merging records - ruby-on-rails

I'm using fields_for(), not for a nested form, but to display a form containing joined items. I need to display data from #seasons, that help me filling drinks...
<% #seasons do |season| %>
<%= fields_for "drinks[]", season.drink do |f| %>
...
<%= f.select :optimized_region_id ... %>
...
<% end %>
<% end %>
It works well.
However, even if #seasons are never the same, some season.drink could be the sames items as they are "parent" relations. (same season.drink.id)
It's not a problem for me.
My issue is that the form sends this:
drinks"=>{
"e80e15c1-a5d4-4df4-80c6-2efa96e39793"=>{"optimized_status"=>"1", "optimized_nickname"=>"Alex"},
"b7501fe0-3a78-412e-88d5-e7643d761a98"=>{"optimized_status"=>"1", "optimized_nickname"=>"Paul"}
...
}
and should send this:
drinks"=>{
"e80e15c1-a5d4-4df4-80c6-2efa96e39793"=>{"optimized_status"=>"1", "optimized_nickname"=>"Alex"},
"e80e15c1-a5d4-4df4-80c6-2efa96e39793"=>{"optimized_status"=>"0", "optimized_nickname"=>"Alex"},
"b7501fe0-3a78-412e-88d5-e7643d761a98"=>{"optimized_status"=>"1", "optimized_nickname"=>"Paul"}
...
}
It seems that Rails is merging drinks that have the same id when the form is sent. Is there a possibility to avoid this and send all drinks even if they have the same drink.id ?

I found a workaround setting the season_id in the [] :
<%= fields_for "drinks[#{season.id}]", record.drink do |f| %>
Now, all my drinks are sent from the form, evene the ones with same id.

Related

How to use models from associations in views with HABTM

I have a #friend model that has_and_belongs_to_many #interests and vice versa. Each interest has a name:string. How do I show all the interests by their name next to each friend?
I tried
friend.interests.count
which shows the correct number, but for
friend.interests.first
the result is
#<Interest:0x00007f959e103250>
How do I display the name of this interest from the database in a view?
<%= friend.interests.count %>
<%= friend.interests.first %>
You can get the name of the interest, just by accessing the friend.interests.first.name. And for listing all the interests you can iterate and show the name of them.
<% friend.interests.each do |interest| %>
<%= interest.name %>
<% end %>
Just put the attribute after the object:
<%= friend.interests.first.name %>

Create form for recursive model

Hello I am trying to create a form in which Model A has many and belong to itself. I tried using nested_form and no luck yet.
I want to create nested form for just Model A.
Can anyone suggest me correct path ?
Without more details on your models and structure it's hard to help, maybe include some code too?
For nested models you typically do something like this:
<%= form_for #model_a do |model_a_form| %>
<%= model_a_form.text_field :attribute_1 %>
<%= model_a_form.text_field :attribute_2 %>
<%= fields_for :child_models, #model_a.child_models do |child_models_fields| %>
<%= child_models_fields.text_field :attribute_1 %>
<%= child_models_fields.text_field :attribute_2 %>
<% end %>
<%= model_a_form.submit %>
<% end %>
See https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for for more info.

Rails habtm checkboxes on create

Is possible to utilize the habtm checkboxes on create action?
because this:
<%= hidden_field_tag "product[size_ids][]", nil %>
<% Size.order(:size).each do |size| %>
<li> <%= check_box_tag "product[size_ids][]", size.id, Product.size_ids.include?(size.id), id: dom_id(size) %>
<%= label_tag dom_id(size), size.size %>
</li>
<% end %>
was on update and was working since was brought to create page rails spits out the
undefined method `size_ids' for #
so, have a way to utilize the habtm on a create action?
Since you're likely dealing with one item, you probably mean:
#product.sizes_ids
The Product model doesn't have a direct association with any sizes, it's only instances of it that do.
use collection check boxes getting all of the model with ids exemple:
<%=p.collection_check_boxes :size_ids, Size.all, :id, :size %>

Capturing errors for individual forms

I am trying to capture validation errors for individual forms as I have many on one page.
I am giving each form its own unique id by using object_id
<% object = #document || Document.new %>
<%= form_for object, :html => { id: object.object_id.to_s } do |f| %>
But if i do this to capture errors the same error message will appear on all my forms
<% if object.errors.any? %>
# errors
<% end %>
I have tried
<% if object.object_id.errors.any? %>
But i get
undefined method `errors' for 59187740:Fixnum
Is there a way around this please
Thanks
Edit
i have just noticed that the form id changes when validation fails as the page reloads, so that explains why the object cannot be found.
How can i keep the form id the same?
If really depends on what you want to do with the errors:
You want to show them for each form:
<%= form_for some_object, do |f1| %>
<%= f1.error_messages %>
<%= # f1 magic %>
<%= form_for other_object, do |f2| %>
<%= f2.error_messages %>
<%= # f2 magic %>
You want to access them for each object:
just call object.errors: some_object.errors
What isn't working for you:
Calling object_id on an object access its id which is a number (FixNum). You are trying to call a method/access an attribute on a FixNum which does not exist.
You just want some persistent ID for your form
I assume that your object is coming from a database and already has an ID. So why don't you just use object.id? Every time Rails create an ActiveRecord object for you from the DB, that object gets a new object_id. So it is only logical that the ID doesn't stay the same since you have a new object in memory. Read more about it here:
https://stackoverflow.com/a/3430487/2295964

Multiple objects in a Rails form

I want to edit multiple items of my model photo in one form. I am unsure of how to correctly present and POST this with a form, as well as how to gather the items in the update action in the controller.
This is what I want:
<form>
<input name="photos[1][title]" value="Photo with id 1" />
<input name="photos[2][title]" value="Photo with id 2" />
<input name="photos[3][title]" value="Custom title" />
</form>
The parameters are just an example, like I stated above: I am not sure of the best way to POST these values in this form.
In the controller I want to something like this:
#photos = Photo.find( params[photos] )
#photos.each do |photo|
photo.update_attributes!(params[:photos][photo] )
end
In Rails 4, just this
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[]", photo do |pf| %>
<%= pf.text_field :caption %>
... other photo fields
UPDATE: This answer applies to Rails 2, or if you have special constraints that require custom logic. The easy cases are well addressed using fields_for as discussed elsewhere.
Rails isn't going to help you out a lot to do this. It goes against the standard view conventions, so you'll have to do workarounds in the view, the controller, even the routes. That's no fun.
The key resources on dealing with multi-model forms the Rails way are Stephen Chu's params-foo series, or if you're on Rails 2.3, check out Nested Object Forms
It becomes much easier if you define some kind of singular resource that you are editing, like a Photoset. A Photoset could be a real, ActiveRecord type of model or it can just be a facade that accepts data and throws errors as if it were an ActiveRecord model.
Now you can write a view form somewhat like this:
<%= form_for :photoset do |f|%>
<% f.object.photos.each do |photo| %>
<%= f.fields_for photo do |photo_form| %>
<%= photo_form.text_field :caption %>
<%= photo_form.label :caption %>
<%= photo_form.file_field :attached %>
<% end %>
<% end %>
<% end %>
Your model should validate each child Photo that comes in and aggregate their errors. You may want to check out a good article on how to include Validations in any class. It could look something like this:
class Photoset
include ActiveRecord::Validations
attr_accessor :photos
validate :all_photos_okay
def all_photos_okay
photos.each do |photo|
errors.add photo.errors unless photo.valid?
end
end
def save
photos.all?(&:save)
end
def photos=(incoming_data)
incoming_data.each do |incoming|
if incoming.respond_to? :attributes
#photos << incoming unless #photos.include? incoming
else
if incoming[:id]
target = #photos.select { |t| t.id == incoming[:id] }
end
if target
target.attributes = incoming
else
#photos << Photo.new incoming
end
end
end
end
def photos
# your photo-find logic here
#photos || Photo.find :all
end
end
By using a facade model for the Photoset, you can keep your controller and view logic simple and straightforward, reserving the most complex code for a dedicated model. This code probably won't run out of the box, but hopefully it will give you some ideas and point you in the right direction to resolve your question.
Rails does have a way to do this - I don't know when it was introduced, but it's basically described here: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers
It took a bit of fiddling to alter the configuration properly for the case where there's no parent object, but this seems to be correct (it's basically the same as gamov's answer, but cleaner and doesn't allow for "new" records mixed in with the "update" records):
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[#{photo.id}]", photo do |pf| %>
<%= pf.text_field :caption %>
... [other fields]
<% end %>
<% end %>
<% end %>
In your controller, you'll end up with a hash in params[:photos], where the keys are photo IDs, and the values are attribute hashes.
You can use "model name[]" syntax to represent multiple objects.
In view, use "photo[]" as a model name.
<% form_for "photo[]", :url => photos_update_path do |f| %>
<% for #photo in #photos %>
<%= render :partial => "photo_form", :locals => {f => f} %>
<%= submit_tag "Save"%>
<% end %>
<% end %>
This will populate input fields just like you described.
In your controller, you can do bulk updates.
def update
Photo.update(params[:photo].keys, params[:photo].values)
...
end
Indeed, as Turadg mentioned, Rack (Rails 3.0.5) fails if you mix new & existing records in Glen's answer.
You can work around this by making fields_for work manually:
<%= form_tag photos_update_path do %>
<% #photos.each_with_index do |photo,i| %>
<%= fields_for 'photos[#{i}]', photo do |pf| %>
<%= pf.hidden_field :id %>
... [other photo fields]
<% end %>
<% end %>
This is pretty ugly if you ask me, but it's the only way I found to edit multiple records while mixing new and existing records.
The trick here is that instead of having an array of records, the params hash gets a array of hashes (numbered with i, 0,1,2, etc) AND the id in the record hash. Rails will update the existing records accordingly and create the new ones.
One more note: You still need to process the new and existing records in the controller separately (check if :id.present?)

Resources