rails 4 nested attributes won't create has_many model - ruby-on-rails-4

I'm new to rails and have spent way too many hours on this. Thanks a lot, in advance, for any help!
I can't seem to get fields_for and/or accepts_nested_attributes_for to work for my nested attributes.
I have a smash_client that has_many contracts and a form that tries to create a smash_client with a parameter and at the same time it tries to also set a parameter on the contract object. The contract belongs_to the smash_client.
I've tried a lot of different solutions and have read the docs but I'm still missing something. I get this in my params hash, in the smash_clients_controller.rb
..., "smash_client"=>{"name"=>"fasdf", "user"=>"adam"}, "smash_client_id"=>{"instance_type"=>"spot"},...
from
= form_for #smash_client do |f|
.field
= f.label :name
= f.text_field :name
.field
= fields_for :smash_client_id do |c|
%p
= c.radio_button :instance_type, 'spot'
= c.label :instance_type, 'spot'
= c.radio_button :instance_type, 'on_demand'
= c.label :instance_type, 'on demand'
.actions
= f.submit 'Save'
and
class SmashClient < ActiveRecord::Base
has_many :contracts, dependent: :destroy
accepts_nested_attributes_for :contracts, allow_destroy: true,
reject_if: proc { |attributes| attributes[:instance_type].blank? }
...
def new
#smash_client = SmashClient.new
3.times { #smash_client.contracts.build }
end
...
def smash_client_params
#smash_client_params = params.require(:smash_client).
permit( :user, :name, contracts_attributes: [:instance_type] )
end
end
and
class Contract < ActiveRecord::Base
belongs_to :smash_client
after_create :determine_instance_type_and_start
before_destroy :stop_instances
...
end
I think the nested params would work if I hard coded it because if I try something like this, in the console, I don't get errors and I get a new SmashClient and Contract.
smash_client_params = {name: 'something', user: 'blah', contracts_attributes: [{instance_type: 'spot'}]}
SmashClient.create( smash_client_params )
I tried using :contracts, #smash_client.contracts and a few other things in the fields_for section. Also tried using select and collection_select but I can't seem to nail down the form.
sorry for the long post. Hopefully I got all the useful info with nothing extra in the question.
I'd really appreciate some direction or answers.
Thanks in advance.

I finally found it. The :instance_type had to be whitelisted in the Contract model. Thanks again, kalyani. I appreciate the help. Here's the changes to the code above:
.field
= fields_for :contracts do |c|
= c.label :instance_type, 'spot instance'
= c.radio_button :instance_type, 'spot', checked: true
= c.label :instance_type, 'on demand instance'
= c.radio_button :instance_type, 'on_demand'
and
def contract_params
params.require(:contract).
permit(:id, :name, :instance_id, :smash_client_id, :instance_type)
end

Instead of : fields_for :smash_client_id do |c|
write it as: fields_for :contracts do |c|
Refer:
1. http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://railscasts.com/episodes/196-nested-model-form-part-1
Rails 4 Nested Attributes Unpermitted Parameters ---- refer this for writing the code in controller and view the correct way

Related

Retrieve method for a polymorphic, has_one and scoped association

Hi this is somewhat it trivial but I can't for the life of me figure out where to make the adjustments.
I have a LoanApplication model and a transfer model like this:
class LoanApplication < ActiveRecord::Base
before_save :populate_guid
belongs_to :user
has_one :loan, -> { where loan: true }, as: :transferable
has_one :repayment, -> { where repayment: true }, as: :transferable
validates_uniqueness_of :guid
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
and
class Transfer < ActiveRecord::Base
belongs_to :transferable, polymorphic: true
belongs_to :user
validates_presence_of :transferable_id,
:transferable_type,
:user_id,
:amount,
:password
end
How come LoanApplication.first.loan gives me the following error message
LoanApplication Load (1.1ms) SELECT "loan_applications".* FROM "loan_applications" ORDER BY "loan_applications"."id" ASC LIMIT 1
NameError: uninitialized constant LoanApplication::Loan
All insights appreciated.
Thanks
I think Application is a reserved word. Try to rename LoanApplication?
It was trivial, I just needed to add class_name: "Transfer" for everything to work. -__-'

Rails & nested comments through partials, strange recursion

I've got a pretty simple setting for (polymorphic) comments model which also have a has_many relationship wih itself (called :replies):
# comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
default_scope -> { order(created_at: :asc) }
belongs_to :user
belongs_to :comment_parent, class_name: "Comment", foreign_key: "comment_id"
has_many :replies, class_name: "Comment", foreign_key: "comment_id"
validates :content, presence: true
validates :commentable, presence: true
end
In the post controller I fetch the comments:
#comments = #commentable.comments.all()
I'm initiating the partial on my blog view page like so:
= render #comments
And finally the comment/_comment partial containing:
(simpiflied code, this only shows gravatar & username)
# comment/_comment.html.haml
%ol.media-list
%li.media{:class => ("media-replied" if comment.comment_id)}
= link_to gravatar_for(comment.user, size: 80, class_name: 'media-object img-circle'), comment.user, class: 'pull-left'
= render partial: 'comments/comment', collection: comment.replies
Somehow this creates some strange recurrence, in which replies get shown on their correct location, but also on an incorrect location. Problem is that there doesn't seem to be any logic in this. (at least for me it doesn't)
I've checked console to verify that the database contains the correct relations between the records (no faulty records), so it must be in the presentation / partial call.
Any ideas?
Ok, that was my own stupid mistake of course... the call #commentable.comments.all() result is all the comments, so even the nested comments are shown as if they are top level.
Short term fixed it by adding where(comment_id: nil)

Rails 4, validating nested models

I have 2 models:
class Person
has_one :user
validates :document, uniqueness: true, on: :create
class User
belongs_to :person
accepts_nested_attributes_for :person
Then i have a form for users that information about the person:
<%= form_for #user do |f| %>
<%= f.fields_for :person do |person_fields| %>
<%= person_fields.label "Document" %>
<%= person_fields.text_field :document %>
<% end %>
<%= f.label "Username" %>
<%= f.text_field :username %>
<% end %>
When i go for /users/edit/1 for example, it loads the user its person attributes. If i change the username and save, it says that the document number is already at use, bypassing the person validation of uniqueness only on create.
Am i missing something here? Are validations like this not supposed to work on nested forms?
Nested attributes allow you to save attributes on associated records through the parent (has_one, has_many), not on the parent through the child (belongs_to).
Your model relationship is not correct; According to Rails api nested attributes are valid for the child through the parent, not the parent through the child as you have specified in your models below.
Your models
class Person
has_one :user
validates :document, uniqueness: true, on: :create
class User
belongs_to :person
accepts_nested_attributes_for :person (sets up nested attributes for parent)
You could set up nested attributes for the child via the parent, as such
Suggested change
class Person
has_one :user
validates :document, uniqueness: true, on: :create
accepts_nested_attributes_for :user
class User
belongs_to :person
Nesting on your form will work only if the nesting associations in the model are set up properly.
Sidebar: The beauty of :Inverse_of
This is a sidebar, but relevant in this context. Inverse_of gives you a reverse reference from the child to the parent. Rails spec for :inverse_of. You should try this out and see how it works for your app. It's a very good thing.
class Person
has_one :user, inverse_of: person
validates :document, uniqueness: true, on: :create
accepts_nested_attributes_for :user
class User
belongs_to :person, inverse_of: user
Debugging questions (added)
I added the on: :create validation to one of my models as a test. I would ask you do a similar test without the model nesting; I think it's informative to test the logic in the model for the on: create, which will validate the controller actions. And only then layer on the nesting attributes. What do you think?
Duplicate field on create => ROLLBACK
Started POST "/locations" for 127.0.0.1 at 2015-07-20 08:03:36 -0500
Processing by LocationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Fxxx", "location"=>{"name"=>"Foo", "food_trailer"=>"true", "street"=>"100 Main", "city"=>"Fredericksburg", "state"=>"Texas", "country"=>"US", "phone"=>"", "website"=>"", "short_desc"=>"A Texan, Ms. Diana has been cookin' up barbecue for 50 years and, spreadin' the “barbecue love” in Paris (France!) since 2010.", "known_for"=>"Tings", "meats_beef"=>"0", "meats_beef_ribs"=>"0", "meats_pork"=>"1", "meats_pork_ribs"=>"0", "meats_chicken"=>"0", "meats_turkey"=>"0", "meats_sausage"=>"0", "meats_venison"=>"0", "sauce"=>"0", "sides"=>"None"}, "commit"=>"Add Restaurant"}
[1m[36mUser Load (0.0ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m [["id", 8]]
[1m[35m (0.0ms)[0m BEGIN
[1m[36mLocation Exists (46.9ms)[0m [1mSELECT 1 AS one FROM "locations" WHERE "locations"."short_desc" = 'A Texan, Ms. Diana has been cookin'' up barbecue for 50 years and, spreadin'' the “barbecue love” in Paris (France!) since 2010.' LIMIT 1[0m
[1m[35mLocation Exists (0.0ms)[0m SELECT 1 AS one FROM "locations" WHERE ("locations"."latitude" IS NULL AND "locations"."longitude" IS NULL) LIMIT 1
[1m[36m (46.9ms)[0m [1mROLLBACK[0m
Duplicate field on edit => COMMIT
Started PATCH "/locations/46" for 127.0.0.1 at 2015-07-20 08:04:30 -0500
Processing by LocationsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxx", "location"=>{"name"=>"Grillrestaurant Rusticana", "street"=>"Grillparzerstraße 5, 81675", "city"=>"München", "state"=>"Alabama", "country"=>"US", "phone"=>"", "website"=>"", "short_desc"=>"A Texan, Ms. Diana has been cookin' up barbecue for 50 years and, spreadin' the “barbecue love” in Paris (France!) since 2010.", "known_for"=>"", "meats_beef"=>"0", "meats_beef_ribs"=>"0", "meats_pork"=>"0", "meats_pork_ribs"=>"0", "meats_chicken"=>"0", "meats_turkey"=>"0", "meats_sausage"=>"0", "meats_venison"=>"0", "sauce"=>"0", "sides"=>""}, "commit"=>"Save changes to Restaurant", "id"=>"46"}
[1m[36mLocation Load (0.0ms)[0m [1mSELECT "locations".* FROM "locations" WHERE "locations"."id" = $1 LIMIT 1[0m [["id", 46]]
[1m[35m (0.0ms)[0m BEGIN
[1m[36mLocation Exists (0.0ms)[0m [1mSELECT 1 AS one FROM "locations" WHERE ("locations"."latitude" = 48.1334073 AND "locations"."id" != 46 AND "locations"."longitude" = 11.6100255) LIMIT 1[0m
[1m[35mSQL (15.6ms)[0m UPDATE "locations" SET "short_desc" = $1, "meats_beef" = $2, "meats_pork" = $3, "meats_beef_ribs" = $4, "meats_pork_ribs" = $5, "meats_chicken" = $6, "meats_turkey" = $7, "meats_sausage" = $8, "meats_venison" = $9, "sauce" = $10, "latitude" = $11, "updated_at" = $12 WHERE "locations"."id" = $13 [["short_desc", "A Texan, Ms. Diana has been cookin' up barbecue for 50 years and, spreadin' the “barbecue love” in Paris (France!) since 2010."], ["meats_beef", "f"], ["meats_pork", "f"], ["meats_beef_ribs", "f"], ["meats_pork_ribs", "f"], ["meats_chicken", "f"], ["meats_turkey", "f"], ["meats_sausage", "f"], ["meats_venison", "f"], ["sauce", "f"], ["latitude", 48.13340729999999], ["updated_at", "2015-07-20 13:04:30.437500"], ["id", 46]]
[1m[36m (46.9ms)[0m [1mCOMMIT[0m

Nested form with nested has_many, and has_one inside

I am essentially running into the same issue as this post, though I have a slightly different situation: has_many nested form with a has_one nested form within it
But as someone else mentioned in that post, the provided answer does not solve the issue.
The relationship is set up so that Invoice has_many items and each Item has_one Modifier. I am attempting to make a single form_for Invoice that allows a use to create many items, each with a Modifier.
MODELS
class Invoice < ActiveRecord::Base
has_many :items
has_many :modifiers, through: :items
accepts_nested_attributes_for :items
end
class Item < ActiveRecord::Base
belongs_to :invoice
belongs_to :modifier
accepts_nested_attributes_for :modifier
end
class Modifier < ActiveRecord::Base
has_one :item
end
CONTROLLER
class Invoice
def new
#invoice = Invoice.new
end
def edit
end
...
end
VIEWS(haml)
invoice.html.haml:
= form_for #invoice do |f|
= f.text_field :status
= f.fields_for :items do |builder|
= render partial: "items/fields", locals: { :f => builder }
= link_to_add_association 'New Item', f, :items, partial: "items/fields", id: "add-item-button"
items/_fields.html.haml:
.nested-fields
- #item = #invoice.items.build
= f.fields_for :modifier, #item.build_modifier do |modifier|
= modifier.text_field :name
Let's review what's happening. In order to build the nested has_one relationship, I build an Item in the nested-fields partial so that I can build the has_one Modifier. This is because rails requires that you explicitly call 'build_something' in a has_one relationship (usually this is called in the controller's new, but I only want to build once someone has clicked the New Item button). For creating new Invoices, this code works perfectly. Checking console, I see that the relationship is created and I can verify that the Modifier was created successfully.
However, when I go back to edit the invoice, cocoon knows that I already have a modifier, so it calls the partial once to create the necessary fields_for my single Modifier. These fields are empty. This makes sense though, because as cocoon is rendering that partial, it is building a new Code with a new modifier and setting the fields blank. I can confirm that is what is occuring because once I have my modifier saved properly, I can go into my partial, remove the two build calls, and view the edit page which properly displays the saved Modifier info just fine.
Of course, now that I've removed my build calls, the form no longer saves any Modifiers that I create. So essentially, I need the build calls in there to build new Modifiers, but I can't have them in there if I want to view them.
Does anyone have a solution to this situation? I have found multiple stack overflow questions and none of them resolves this issue.
You say has_one but in your model I see has_many ?
You nested partial items/_fields is wrong: you build an extra item and there is not need for that. Coccon, in the link_to_add_association builds a new item to insert.
There are two ways to do what you want to do.
1) In the partial
To handle it correctly in your partial you can do the following (items/_fields.html.haml) :
.nested-fields
- f.object.build_modifier if f.object.new_record?
= f.fields_for :modifier do |modifier|
= modifier.text_field :name
In rails, to refer to the object of the form you can use f.object. Note this will work, but we have to check if it is a newly created object. Alternatively we could just check if the modifier is present.
2) Using the :wrap_object option ( documentation )
Cocoon allows to execute some extra code with the newly created object. So in your case that would become:
= link_to_add_association('New item', f, :items,
:wrap_object => Proc.new { |item| item.build_modifier; item })

Rails - Multiple Nested Attributes

I am trying to use a select tag to save multiple nested children in a parent.
This is the error I'm looking at
Couldn't find all UserLocations with IDs (1, 2) (found 0 results, but was looking for 2)
I have the following setup under Rails 4 and Devise:
User
class User < ActiveRecord::Base
has_many :user_locations
accepts_nested_attributes_for :user_locations, :allow_destroy => true
end
UserLocation (locations that the user has)
class UserLocation < ActiveRecord::Base
belongs_to :user
belongs_to :location
end
Location (predefined list of locations the user can choose upon)
class Location < ActiveRecord::Base
has_many :user_locations
has_many :users, through: :user_locations
end
However, when trying to save the selected UserLocations, they won't be saved.
Rails Select Tag (You can choose multiple items)
<%= f.select :user_location_ids, options_for_select(Location.all.collect { |l| [ l.name, l.id ] }, #user.user_locations.collect{ |l| l.id }), {}, { multiple: true } %>
I have put the user_location_ids in my application_controller as user_location_ids: []
Cheers
Solved
The solution is to overwrite the default setter method for multiple nested models model_ids=(value). Do not use the plural of the model, e.g. models_ids=(value), because that is wrong!
def user_location_ids=(value)
for slot in value do
unless slot == ""
location = Location.find_by(id: slot.to_i)
unless location.nil?
self.user_locations << UserLocation.create(user_id: self.id, location_id: location.id)
end
end
end
end