ruby on rails - presenting many to many relation - ruby-on-rails

I have recipe and ingredient in a many to many relation.
I have defined the following commands in my presentation.
<div>
<%= render :partial => 'ingredients/form',
:locals => {:form => recipe_form} %>
</div>
the partial begins with
<%= form_for(#ingredient) do |ingredient_form| %>
but received #ingredient nill.
Then I tried
<%= recipe_form.fields_for :ingredients do |builder| %>
<%= render 'ingredient_fields', f: builder %>
<% end %>
where my render was
<p class="fields">
<%= f.text_field :name %>
<%= f.hidden_field :_destroy %>
</p>
but nothing was printed.
then I tried
<% #recipe.ingredients.each do |ingredient| %>
<%= ingredient.name %>
<% end %>
and only then all of the ingredients were printed.
What was I doing wrong in the previous tries ?
Thank you.
my ingredient recipe relation defined as follows
class Ingredient < ActiveRecord::Base
has_many :ingredient_recipes
has_many :recipes, :through => :ingredient_recipes
...
class Recipe < ActiveRecord::Base
has_many :ingredient_recipes
has_many :ingredients, :through => :ingredient_recipes
...
accepts_nested_attributes_for :ingredient_recipes ,:reject_if => lambda { |a| a[:content].blank?}
class IngredientRecipe < ActiveRecord::Base
attr_accessible :created_at, :ingredient_id, :order, :recipe_id
belongs_to :recipe
belongs_to :ingredient
end

You don't exactly specify what you are trying to do, so I am presuming you have a page that shows a recipe, with many ingredients that can be edited and added to. In your controller you have something like:
class RecipeController < ApplicationController
def edit
#recipe = Recipe.find(params[:id]
end
end
I am also presuming that you are looking to have a form that post backs to the create action. I therefore think you want a form like this:
<%= form_for #recipe do |form| %>
<%= label_for :name %>
<%= text_field :name %>
<%= form.fields_for :ingredients do |ingredients_fields| %>
<div class="ingredient">
<%= f.text_field :name %>
<%= f.hidden_field :_destroy %>
</div>
<% end %>
<% end %>
Also, change your recipe to accept nested attributes for ingredients, not ingredient_recipes:
class Recipe < ActiveRecord::Base
has_many :ingredient_recipes
has_many :ingredients, :through => :ingredient_recipes
...
accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:content].blank?}
And finally, add attr_accessible for your content:
class Ingredient < ActiveRecord::Base
attr_accessible :content
...
Does that work for you?

Related

Rails 4.2 Create ActiveRecord object with nested params

Does anybody know how to handle these attributes in the controllers new and create action?
(byebug) params
{"utf8"=>"V", "authenticity_token"=>"Wex9nnFigOviySzjfPN6zw==",
"recipe"=>{"name"=>"Cupcake"},
"name"=>{"ingredient_id"=>"2"},
"quantity"=>{"amount"=>"zwei"},
"commit"=>"Create", "controller"=>"recipes", "action"=>"create"}
I don't know how to create the quantities record with the value from Ingredient in recipe#new
("name"=>{"ingredient_id"=>"2")
Two records should be created (Recipe and Quantity).
Recipe
name
Quantity
ingredient_id
recipe_id
amount
Ingredient
name
class Recipe < ActiveRecord::Base
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :ingredients
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, :through => :quantities
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredient, :reject_if => :all_blank
end
<%= form_for(#recipe) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= form_for(#recipe.quantities.build) do |g| %>
<%= select("name", "ingredient_id", Ingredient.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'Auswählen'}) %>
<%= g.label :amount %>
<%= g.text_field :amount %>
<%= g.submit "Create" %>
<% end %>
<% end %>
I replaced:
<%= form_for(#recipe.quantities.build) do |g| %>
with:
<%= f.fields_for :quantities, #recipe.quantities.build do |g| %>
And put submit action to form level after fields_for:
<%= f.submit "Create" %>

Rails uninitialized constant for an association

So there is a plethora of questions about the "uninitialized constant" error, and it's almost always due to an incorrectly specified association (e.g. plural model names instead of singular, incorrectly writing your association inside the model, etc). My models and form look spotless, so maybe this is something new (or I'm blind)?
A "user" has one "move". A "move" has many "neighborhood_preferences", and through this, many "neighborhoods".
Models:
class User < ActiveRecord::Base
has_one :move
accepts_nested_attributes_for :move, allow_destroy: true
end
class Move < ActiveRecord::Base
belongs_to :user
has_many :neighborhood_preferences
has_many :neighborhoods, through: :neighborhood_preferences
accepts_nested_attributes_for :neighborhood_preferences, allow_destroy: true
end
class NeighbhoodPreference < ActiveRecord::Base
belongs_to :neighborhood
belongs_to :move
end
class Neighborhood < ActiveRecord::Base
belongs_to :city
has_many :neighborhood_preferences
has_many :moves, through: :neighborhood_preferences
end
View:
<%= simple_form_for(#user, :html => { class: :form } ) do |u| %>
<%= u.fields_for :move do |m| %>
<div>
<%= m.label :start_date %>
<%= m.date_field :start_date %>
</div>
<div>
<%= m.label :end_date %>
<%= m.date_field :end_date %>
</div>
<div>
<%= m.label :min_price %>
<%= m.text_field :min_price %>
</div>
<div>
<%= m.label :max_price %>
<%= m.text_field :max_price %>
</div>
<%= m.association :neighborhood_preferences %>
<% end %>
<%= u.submit "Save Changes" %>
<% end %>
There is a typo in class name NeighbhoodPreference.

form_for with ckeckbox_tag from other model

I'm new to rails and just cant get that problem solved.
i have 3 models. Orders, Products and LineItems.
I want to have a order form with checkboxes for each product. User selects appropriate products and submits the order.
I cannot get the form to create the correct hash.
class Order < ActiveRecord::Base
attr_accessible :account_id, :user_id
has_many :line_items, :dependent => :destroy
end
class LineItem < ActiveRecord::Base
attr_accessible :account_id, :product_id, :order_id
belongs_to :orders
belongs_to :product
end
Here the view:
<%= form_for 'line_items[]' do |f| %>
<%= f.select :account_id, options_from_collection_for_select( Account.all,
:id, :name ), :prompt => 'Select Account' %>
<% Product.all.each do |product| %>
<div>
<%= check_box_tag 'line_items[product_ids][]', product.id %>
</div>
<% end -%>
<div>
<%= f.submit 'save' %>
</div>
thanks!
You would need to use accepts_nested_attributes_for in your model to enable nested atributes from associated models. You may also want to check out this railscast and adapt to your needs.
For example in the orders model:
class Order < ActiveRecord::Base
attr_accessible :account_id, :user_id
has_many :products #This makes the association to products
has_many :line_items, :dependent => :destroy
accepts_nested_attributes_for :products #This allows the attributes from products accessible
end
Then the form could be:
<%= form_for #order do |f| %>
<%= f.select :account_id, options_from_collection_for_select( Account.all,
:id, :name ), :prompt => 'Select Account' %>
<%= f.fields_for :product do |product_form| %>
<%= product_form.check_box :id %>
<% end %>
<%= f.submit %>
<% end %>

Multiple polymorphic fields in one form

class User < ActiveRecord::Base
belongs_to :person, :dependent => :destroy
accepts_nested_attributes_for :person, :allow_destroy => true
attr_accessible :person_attributes
end
class Person < ActiveRecord::Base
has_many :phone_numbers, :as => :phoneable, :dependent => :destroy
has_one :user
accepts_nested_attributes_for :phone_numbers
end
class PhoneNumber < ActiveRecord::Base
belongs_to :phoneable, :polymorphic => true
end
<%= form_for #user do |user_form| %>
<%= user_form.fields_for :person do |person_form| %>
<%= person_form.fields_for :phone_numbers do |phone_number_form| %>
<%= phone_number_form.text_field :number %>
<% end %>
<% end %>
<% end %>
This works. It does what I expect, but I want more than one phone number in my form. How can I accomplish that?
user[person_attributes][phone_numbers_attributes][0][number]
Why does fields_for add [0] ?
If I want multiple phone numbers, would the second look like this?
user[person_attributes][phone_numbers_attributes][1][number]
If so, how?
If I can get multiple phone numbers in the database, my next question will be how to include other phone number attributes along with each number? e.g.: description
user[person_attributes][phone_numbers_attributes][0][number]
user[person_attributes][phone_numbers_attributes][0][description]
fields_for adds "[0]" because its a many relationship and it needs to make an array, with an index for each relation member [0], [1] ...
So yes, the second would have [1], rails adds that by itself via the helpers.
To include other phone number attributes:
<%= form_for #user do |user_form| %>
<%= user_form.fields_for :person do |person_form| %>
<%= person_form.fields_for :phone_numbers do |phone_number_form| %>
<%= phone_number_form.text_field :number %>
<%= phone_number_form.text_field :description %>
<% end %>
<% end %>
<% end %>

accepts_nested_attributes_for and collection_select how to build the view?

I have a model Journey which has many Users (drivers). I want to be able with help of accepts_nested_attributes_for to add and remove drivers from a journey. When I add a driver I want to show the user a <select> where she can select one of the users to be one of the drivers belonging to that particular journey. I have come that long:
# Models
class Journey < ActiveRecord::Base
has_many :drivers
accepts_nested_attributes_for :drivers, :allow_destroy => true
has_many :users, :through => :drivers
accepts_nested_attributes_for :users
end
class Driver < ActiveRecord::Base
belongs_to :journey
belongs_to :user
end
class User < ActiveRecord::Base
has_many :drivers
has_many :journeys, :through => :drivers
end
# View _form.html.erb
<% form_for(#journey) do |f| %>
<%= f.error_messages %>
<% f.fields_for :drivers do |d| %>
<%= render :partial => 'driver', :locals => { :f => d } %>
<% end %>
<p><%= f.submit 'Submit' %></p>
<% end %>
# View _driver.html.erb
<p><%= f.collection_select(:id, User.all, :id, :name)%></p>
The error says:
ActiveRecord::AssociationTypeMismatch in JourneysController#create
Driver(#2185315860) expected, got Array(#2151950220)
I suspect that my _driver.html.erb is wrong, but I have no idea how to fix it. Could you please help me out with some hints here?
Your _driver.html.erb should look like this:
<%= f.collection_select(:user_id, User.all, :id, :name) %>
But I'm not sure if this causes the error.
Also when I use accepts_nested_attributes_for for nested models, I do it this way:
# Models
class Journey < ActiveRecord::Base
has_many :drivers
accepts_nested_attributes_for :drivers, :allow_destroy => true
has_many :users, :through => :drivers
end
class Driver < ActiveRecord::Base
belongs_to :journey
belongs_to :user
accepts_nested_attributes_for :users
end
So you can have forms like this:
<% form_for #journey do |f| %>
<% fields_for :drivers do |d| %>
<% fields_for :user do |u| %>
<%= u.text_field :name %>
...
<% end %>
<% end %>
<% end %>

Resources