Ruby on rails: fields_for do nothing if defined submodel_attributes= - ruby-on-rails

I have such code in new.erb.html:
<% form_for(#ratification) do |f| %>
<%= f.error_messages %>
<% f.fields_for :user do |fhr| %>
<p>
<%= fhr.label :url %><br />
<%= fhr.text_field_with_auto_complete :url %>
</p>
<% end %>
<% end %>
If i have empty Ratification.rb it is ok, fields_for works ok.
But if I wrote:
class Ratification < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
or
class Ratification < ActiveRecord::Base
belongs_to :user
def user_attributes=(attr)
...
end
end
f.fields_for yields nothing! Why!?
Rails: 2.3.8
Plugin for autocomplete: repeated_auto_complete

just add an = (EQUAL) after <% in the f.fields_for
like this:
<%= f.fields_for :user do |fhr| %>
<p>
<%= fhr.label :url %><br />
<%= fhr.text_field_with_auto_complete :url %>
</p>
<% end %>
obs: you have to do the same in Rails 3

I believe you need to build a user in your controller, like
# controller
def new
#ratification = Ratification.new
#ratification.build_user
end
What about
<% f.fields_for :user, #ratification.user do |fhr| %>
# ...
<% end %>
?
I believe if you use
<% f.fields_for :user do |fhr| %>
you should have #user as instance variable. But in your case you have #ratification.user.

You cannot redefine user_attributes since you will overwrite the standar behaviour that ActiveRecord specifies for it. If still knowing this you need to redefine user_attributes try using alias_method_chain.

Aren't you doing this wrong? If Ratification belongs to a user, then the user model should accept nested attributes for ratifications, not the other way around.
So if users have many ratifications, and if you want to submit multiple ratifications for a user in a single form, then you would use accept nested attributes for ratification in the user model.
And you would do somewhere in the users controller
#user = User.new
2.times { #user.ratifications.build } # if you want to insert 2 at a time
I tried to do something similar in the console:
#user = User.new
#user.ratifications.build # this works
But if I did
#ratification = Ratification.new
#ratification.user.build # this fails

I just encountered the same problem and was able to fix it. The problem was that fields_for takes :user as an argument while the argument should be #ratification.user
Hence, replace
<% f.fields_for :user do |fhr| %>
with
<% f.fields_for #ratification.user do |fhr| %>
That's it.

Related

Rails/ActiveRecord - association not saving

I can't get my CheckIn record to save because the associated Tenancy isn't saving.
I have three models with associations:
class Property < ApplicationRecord
has_many :tenancies
end
class Tenancy < ApplicationRecord
belongs_to :property
has_many :check_ins
end
class CheckIn < ApplicationRecord
belongs_to :tenancy
accepts_nested_attributes_for :tenancy
end
I want the CheckIn new action to create both the CheckIn and the associated Tenancy:
def new
#check_in = CheckIn.new
#check_in.build_tenancy.property_id = params[:property_id]
end
I have to include the property_id part otherwise the Tenancy won't save.
The form in check_ins/new.html.erb:
<%= form_for #check_in, url: property_check_ins_path do |f| %>
<%= f.label :date_time %>
<%= f.datetime_select :date_time, {minute_step: 15} %>
<%= f.label :tenancy %>
<%= f.fields_for :tenancy do |i| %>
<%= i.date_select :start_date %>
<% end %>
<%= f.submit "Create Check In" %>
<% end %>
I've added tenancy attributes to the strong params in the CheckInsController:
def check_in_params
params.require(:check_in).permit(:tenancy_id, :date_time, tenancy_attributes: [:start_date])
end
It's worth noting that the check_ins routes are nested in properties:
resources :properties do
resources :check_ins, only: [:new, :create]
end
So the problem is that by the time I get to the create action in the CheckInsController, the tenancy that I built has disappeared. I'm not sure how and when each of the records should be being saved and the slight complexity of what I'm trying to achieve has made it quite difficult to find relevant help so any ideas?
I'm using Rails 5.
The problem was that the property attached to the tenancy was being forgotten. I removed the property attachment from the new action:
def new
#check_in = CheckIn.new
#check_in.build_tenancy
end
Added a hidden field for property_id to the form (as well as adding :property_id to the strong params):
<%= f.fields_for :tenancy do |i| %>
<%= i.date_select :start_date %>
<%= i.hidden_field :property_id, value: params[:property_id] %>
<% end %>
And saved the tenancy in the CheckIn create action, prior to saving the check in itself:
def create
#check_in = CheckIn.new(check_in_params)
#check_in.tenancy.save
if #check_in.save
redirect_to property_check_in_path(#check_in.tenancy.property.id, #check_in)
else
render :new
end
end
I'd certainly be interested if anyone could pick holes in this solution or offer a better one.
Using nested resources (check_ins depends from properties) you create a namespaces routes. form_for helper ( rails guides - form helpers ) when you build your form, need a Property reference also.
I try to explain me better with an example:
#checks_controller.rb
def new
#property = Property.new
#check_in = #property.build_check_ins
#check_in.build_tenancy
end
#check_ins/new.html.erb
<%= form_for [#property, #check_in], url: property_check_ins_path do |f| %>
<%= f.label :date_time %>
<%= f.datetime_select :date_time, {minute_step: 15} %>
<%= f.label :tenancy %>
<%= f.fields_for :tenancy do |i| %>
<%= i.date_select :start_date %>
<% end %>
<%= f.submit "Create Check In" %>
<% end %>
I haven't tried this code, but I hope this give you at least a way to follow to solve your problem.

How to access value of child model inside nested attributes form

I have two models post and sign like this.
class Post < ActiveRecord::Base
has_many :signs
accepts_nested_attributes_for :signs
end
class Sign < ActiveRecord::Base
belongs_to :post
end
And I use nested_form gem, and this is form for post
<%= nested_form_for(#post) do |f| %>
...
<%= f.fields_for :signs do |sign| %>
<%= render 'sign_fields', :f => sign %>
<% end %>
...
<% end %>
And this is _sign_fields.html.erb.
<div class="sign">
<%= image_tag "#{"%02d" % #post.signs[f.options[:child_index].to_i].image_number}.jpg" %>
</div>
It works though, I think there is a better way than #post.signs[f.options[:child_index].to_i].image_number.
How can I access the attributes of child model?
You can use
f.object.image_number

Rails 4 - checkboxes for has_and_belongs_to_many association

I recently had a problem getting checkboxes to work for a has_and_belongs_to_many (HABTM) association in Rails 4. I was able to find the information on how to get it working correctly in a few disparate places, but thought it would be good to document the few simple steps necessary to get it working correctly in one place here on StackOverflow.
As a setup assume a model of Kennel with a HABTM association to Handler.
class Kennel
has_and_belongs_to_many :handlers
end
This is all you need to do for the form: Don't do it manually when there is a built in helper.
<%= form_for #kennel do |f| %>
<%= f.collection_check_boxes(:handler_ids, Handler.all, :id, :to_s) %>
<% end %>
The form should have something like this:
<%= form_for(#kennel) do |form| %>
...
<div class="field">
<div class="field_head">Handlers</div>
<%= hidden_field_tag("kennel[handler_ids][]", nil) %>
<% Handler.order(:name).each do |handler| %>
<label><%= check_box_tag("kennel[handler_ids][]", id, id.in?(#kennel.handlers.collect(&:id))) %> <%= handler.name %></label>
<% end %>
</div>
...
<% end %>
The hidden_field_tag allows the user to uncheck all the boxes and successfully remove all the associations.
The controller needs to allow the parameter through strong parameters in the permitted_params method:
params.permit(kennel: [:city, :state
{handler_ids: []},
:description, ...
])
References:
http://railscasts.com/episodes/17-habtm-checkboxes
https://coderwall.com/p/_1oejq
I implement has_and_belongs_to_many association this way:
model/role
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
model/user
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
end
users/_form.html.erb
---
----
-----
<div class="field">
<% for role in Role.all %>
<div>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%= role.name %>
</div>
<% end %>
</div>
users_controller.rb
def user_params
params.require(:user).permit(:name, :email, { role_ids:[] })
end
Intermediate table_name should be roles_users and there should be two fields:
role_id
user_id

Pre-populated association for nested form rendered empty

My PlanList has_many :items.
I want my plan_list#new to have a nested form, where I can pre-populate items.
I tried
# View
<%= form_for #plan_list do |f| %>
<%= f.fields_for :items do |item| %>
<%= item.text_field :quantity %>
<% end %>
with
# Controller
def new
#plan_list = PlanList.new
#plan_list.items.build(quantity:1)
#plan_list.items.build(quantity:2)
end
However I only see empty inputs for items.
I also tried <%= f.fields_for #plan_list.items do |item| %> but it will only show one item (the last one with quantity 2). How can I achieve my goal?
You should use the build method of associations as:
def new
#plan_list = PlanList.new
#plan_list.items << #plan_list.items.build(quantity:1)
#plan_list.items << #plan_list.items.build(quantity:2)
end
I did not have accepts_nested_attributes_for :items.
I thought I could add this later when I implement the create method, but it is essential for both form creation and attribute mass assignment
Yep, this is must and should resolve the issue accepts_nested_attributes_for :items
<%= form_for #plan_list do |f| %>
<%= f.fields_for :items do |item| %>
<%= item.text_field :quantity %>
<% end %>
def new
#plan_list = PlanList.new
#plan_list.items.build(quantity:1)
#plan_list.items.build(quantity:2)
end

Nested model form with mutliple has_many/belongs_to associations

I have three models:
class Rate < ActiveRecord::Base
attr_accessible :user_id, :car_id, :rate
belongs_to :user
belongs_to :car
end
class User < ActiveRecord::Base
attr_accessible :name
has_many :rates
accepts_nested_attributes_for :rates
end
class Car < ActiveRecord::Base
attr_accessible :name
has_many :rates
accepts_nested_attributes_for :rates
end
And one controller:
class UsersController < ResourceController
def new
# Assume user is loaded
#user.rates.build
end
end
I'm trying to build a nested form that will associate a list of users/cars and their associated rates.
Something like:
<% form_for #user do |f| %>
<%= #user.name %><br />
<% Car.all.each do |car| %>
<%= car.name %><br />
<%= f.fields_for :rates do |r| %>
<%= r.number_field :rate %>
<% end %>
<% end %>
<% end %>
The problem is that I would like the Rate model to store data as follows:
USER_ID CAR_ID RATE
1 1 10
1 2 20
1 3 30
2 1 40
3 2 50
I cannot figure out how to properly build the fields_for helper to build the proper params for both the user_id and the car_id.
Something like:
user[car=1][rate]
user[car=2][rate]
I've tried being more explicit with the fields_for like this:
<%= r.fields_for 'user[car][rate]' %>
But it still doesn't build out the nested parameters properly. The car parameter is not correctly identified.
Any help would be appreciated! Thanks.
EDIT:
The controller action has to be under user. The example above has been shortened for brevity but other user-related attributes are available through the form so it has to use the users controller.
ANSWER:
I figured out a way to do it. I've added my own answer that explains it.
<% form_for #user do |f| %>
<%= #user.name %><br />
<%= f.fields_for :rates do |r| %>
<% Car.all.each do |car| %>
<%= car.name %><br />
<%= r.number_field :rate %>
<% end %>
<% end %>
<% end %>
This may be solution of your problem. Just check it.
The form is going to create a new rate instead of a new user, so the method should be in RatesController instead of UsersController.
With this logic the problem seems solved. You can write field_for rate[user] and field_for rate[car]
I think I've got it figured out.
In my controller, I've modified the build method as follows:
Car.all.each { |c| #user.rates.build(car_id: c.id) } if #user.rates.count == 0
Then, in my model, I need the following:
attr_accessible :rates_attributes
Finally, the fields_for block should look like this (remember, this is in the #user form object f):
<%= f.fields_for :rates do |r| %>
<%= r.hidden_field :car_id %>
<%= r.object.car.name %><br />
<%= r.number_field :rate %>
<% end %>
This builds the params hash properly and create the rate model entries when the form is submitted.
The check on existing user rates in the controller will ensure that the existing values are used in the form and new ones are not built (which I thought build took into consideration... ?).

Resources