I'm having real trouble getting my head around editing attributes in has_many through join models. I've set up a very simple app to experiment with; Recipes, Ingredients and Recipe_Ingredients (the join).
Can anyone help with making this work as it should? As it is, it'll pulling through 'qty' from the join model, but not the actual ingredient.
I've put a public repo up that anyone can download to play with: https://github.com/EssentialMusic/Recipes
The models:
class Ingredient < ActiveRecord::Base
attr_accessible :name
has_many :recipe_ingredients, :dependent => :destroy
has_many :recipes, :through => :recipe_ingredients
end
class Recipe < ActiveRecord::Base
attr_accessible :author, :description, :name, :recipe_ingredients_attributes, :ingredients_attributes
has_many :recipe_ingredients, :dependent => :destroy
has_many :ingredients, :through => :recipe_ingredients
accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => :true
accepts_nested_attributes_for :recipe_ingredients
end
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
attr_accessible :measure, :qty, :special_instructions
end
The form
<%= form_for(#recipe) do |f| %>
<% if #recipe.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#recipe.errors.count, "error") %> prohibited this recipe from being saved:</h2>
<ul>
<% #recipe.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :author %><br />
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description %>
</div>
<div>
<%= f.fields_for :recipe_ingredients do |ri| %>
<%= ri.text_field :qty %> -
<%= ri.fields_for :ingredients do |i| %>
<%= i.text_field :name %><br>
<% end %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Cheers!!
Substitute the ri with f in the second nested fields_for like this:
<%= f.fields_for :recipe_ingredients do |ri| %>
<%= ri.text_field :qty %> -
<%= **f**.fields_for :ingredients do |i| %>
<%= i.text_field :name %><br>
<% end %>
<% end %>
Related
I have a User model as below,
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable
has_many :company_users
accepts_nested_attributes_for :company_users, :allow_destroy => true
has_many :companies, :through => :company_users
has_many :roles, :through => :company_users
end
and its associated model CompanyUser as below,
class CompanyUser < ActiveRecord::Base
belongs_to :user
belongs_to :company
belongs_to :role
end
I am trying to build the associations as below but it seems like not working
# GET /users/new
def new
#user = User.new
#user.company_users.build
end
View file is as follows,
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<h3>Companies and Roles</h3>
<div class="field">
<% f.fields_for :company_users do |cu| %>
<p>
<%= cu.label :company_id %>
<%= cu.text_field :company_id%>
<%= cu.label :role_id %>
<%= cu.text_field :role_id %>
<%= cu.check_box :_destroy %>
<%= cu.label :_destroy, 'delete' %>
</p>
<% end %>
<p>
<%= f.submit 'Add to user', :name => "add_company_user" %>
<%= f.submit 'Delete from user', :name => "remove_company_user" %>
</p>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to figure out where it is going wrong.
You are missing a =, therefore the fields_for block isn't rendered to the page. Change this line
<% f.fields_for :company_users do |cu| %>
to
<%= f.fields_for :company_users do |cu| %>
I have scoured all the other questions relating to this topic here, but I am still having this problem. I have a nested form inside a nested form. Answer choices are inside questions which are inside survey. I had no problem setting up the questions, and they save fine. The Answer choices, though, are not saving, and I cannot figure out where I am going wrong.
survey.rb
class Survey < ActiveRecord::Base
belongs_to :author
has_many :questions, dependent: :destroy
has_many :forms, dependent: :destroy
has_many :answer_choices, through: :question
validates :name, uniqueness: true
accepts_nested_attributes_for :questions, reject_if: proc { |attributes| attributes['text'].blank?},
allow_destroy: true
end
question.rb
class Question < ActiveRecord::Base
belongs_to :survey
has_many :responses, dependent: :destroy
has_many :answer_choices, dependent: :destroy
accepts_nested_attributes_for :answer_choices, reject_if: proc { |attributes| attributes['content'].blank?},
allow_destroy: true
end
answer_choice.rb
class AnswerChoice < ActiveRecord::Base
belongs_to :question
end
survey_controller.rb
def new
#survey = Survey.new(author_id: session[:user_id])
#survey.questions.build
#survey.questions.each { |question| 4.times { question.answer_choices.build }}
end
def edit
#survey.questions.build
#survey.questions.each { |question| 4.times { question.answer_choices.build }}
end
def survey_params
params.require(:survey).permit(:name, :description, :author_id,
questions_attributes: [:id, :text, :required, :response_type,
:_destroy, :number])
end
_form.html.erb
<%= form_for(#survey) do |f| %>
<% if #survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% #survey.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.hidden_field :author_id %>
</div>
<h2>Questions</h2>
<%= f.fields_for(:questions) do |ff| %>
<%= render 'questions', :f => ff %>
<% end %>
<div class="actions">
<%= f.submit 'Submit Question'%>
</div>
<div class="actions">
<%= link_to 'Finish Survey', surveys_path %>
</div>
<% end %>
_questions.html.erb
<div>
<span><%= f.index + 1 %>. </span>
<div class="field">
<%= f.hidden_field :number, value: f.index + 1 %>
</div>
<div class="field">
<%= f.label :text, "Question" %><br>
<%= f.text_field :text %>
</div>
<div class="field">
<%= f.label :response_type %><br>
<%= f.select :response_type, [["Yes/No", "yes/no"], ["Short Answer", "string"], ["Long Answer", "text"], ["Multiple Choice", "multi"]] %>
</div>
<div class="field">
<%= f.label :required %>
<%= f.check_box :required %>
</div>
<div>
<p>Answer Choices</p>
<%= f.fields_for(:answer_choices) do |ff| %>
<%= render 'answer_choices', :f => ff %>
<% end %>
</div>
<div class="field">
<%= f.label :_destroy %>
<%= f.check_box :_destroy %>
</div>
</div>
_answer_choices.html.erb
<div class="field">
<%= f.label :content %><br>
<%= f.text_field :content %>
</div>
<div class="field">
<%= f.label :_destroy %>
<%= f.check_box :_destroy %>
</div>
A few changes in your code.
Change your new method like this
def new
#survey = Survey.new(author_id: session[:user_id])
#questions = #survey.questions.build
#answer_choices = 4.times { #questions.answer_choices.build }
end
Your survey_params should look like this
def survey_params
params.require(:survey).permit(:name, :description, :author_id,
questions_attributes: [:id, :text, :required, :response_type,
:_destroy, :number, answer_choices_attributes: [:id,:content,:_destroy]])
end
And in your form code, change these lines
<%= f.fields_for(:questions) do |ff| %>
<%= f.fields_for(:answer_choices) do |ff| %>
into
<%= f.fields_for #questions do |ff| %>
<%= f.fields_for #answer_choices do |ff| %>
Following the railscast #196 on nested forms... I have the following models:
class DealContact < ActiveRecord::Base
belongs_to :deal
belongs_to :contact
class Contact < ActiveRecord::Base
has_many :deal_contacts
has_many :deals, through: :deal_contacts
accepts_nested_attributes_for :deal_contacts, :allow_destroy => true
class Deal < ActiveRecord::Base
has_many :deal_contacts
has_many :contacts, through: :deal_contacts
accepts_nested_attributes_for :deal_contacts, :allow_destroy => true
In my deals form I have this
<div class="row">
<div class="span12"><h4>Contacts Associated with this Deal</h4></div>
<%= f.fields_for :deal_contacts do |builder| %>
<%= render 'deal_contact_fields', f: builder %>
<% end %>
<div class="span1"><%= link_to_add_contact "Add", f, :deal_contacts %></div>
</div>
</div>
And deal_contact_fields just contains:
<fieldset>
<div class="span4">
<%= f.association :contact, collection: Contact.all(order: 'contact_name'), label_method: :full_desc %>
</div>
<div class="span6">
<%= f.label :details, "Details " %>
<%= f.text_field :details %>
</div>
<div class="span1" style="margin-top: 30px">
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</div>
</fieldset>
This all works great for editing existing deals, however when I create a new one I get the following error:
undefined method `association' for #<ActionView::Helpers::FormBuilder:0x007fe6fba55840>
:-(
.association is only available in simple_form gem
You'll have to use collection_select to achieve what you want without that gem:
<%= f.collection_select :contact, Contact.all(order: 'contact_name'), :id, :name %>
Trying to do a many to many relations ship.
I have two models, Teams and Matches and i'm doing a many-to-many relations ship but getting this error when trying to save a nested model.
'Couldn't find Team with ID=2 for Match with ID='
I know that my model is new so it doesn't have an id yet, but i'm using the Match.new(params[:match] method which should work.
Team:
class Team < ActiveRecord::Base
attr_accessible :description, :name, :status
attr_protected :id
has_many :matchships
has_many :matches, :through => :matchships
end
Match:
class Match < ActiveRecord::Base
attr_accessible :date, :name
attr_protected :id
has_many :matchships
has_many :teams , :through => :matchships
accepts_nested_attributes_for :teams
end
MatchShips:
class Matchship < ActiveRecord::Base
attr_accessible :match_id, :team_id
belongs_to :match
belongs_to :team
end
Match Controller:
New:
def new
#match = Match.new
#match.teams.build
end
Create:
def create
'failing here' --------> #match = Match.new(params[:match])
#team = Team.find(params[:team_id])
#match.teams << #team
#team.matches << #match
Form:
<%= nested_form_for(#match) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :date %><br />
<%= f.date_select :date %>
</div>
<%= f.fields_for :teams, :html => { :class => 'form-vertical' } do |builder| %>
<%= builder.label "Team Name:" %>
<%= builder.autocomplete_field :name, autocomplete_team_name_teams_path, :update_elements => {:id => "##{form_tag_id(builder.object_name, :id)}" },:class => "input-small",:placeholder => "Search" %>
<%= builder.hidden_field :id %>
<% end %>
<%= f.link_to_add raw('<i class="icon-plus-sign"></i>'), :teams, :class => 'btn btn-small btn-primary' %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controller
I am trying to use the accepts_nested_attributes_for method within my model, however I need to render the records grouped by another association. I have got this to work but the method I have used seems like a bit of a hack.
Is there a better way to structure this?
My Model
has_many :quantities
has_many :ingredients, :through => :quantities, :uniq => true
has_many :sizes, :through => :quantities, :uniq => true
has_many :photos, :as => :imageable
accepts_nested_attributes_for :quantities
My View
<%= form_for [:admin, #recipe] do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<% #recipe.quantities.group_by(&:size).each do |size, quantities| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities do |builder| %>
<% if builder.object.size == size %>
<p>
<%= builder.text_area :value, :rows => 1 %>
</p>
<% end %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You can get rid of the if builder.object.size == size part with this :
<% #recipe.quantities.group_by(&:size).each do |size, quantities_for_size| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities, quantities_for_size do |builder| %>
<p><%= builder.text_area :value, :rows => 1 %></p>
<% end %>
<% end %>
passing the quantities_for_size as a second argument to fields_for should make it use it instead of the whole quantities associated to the recipe. See the docs on #fields_for for more information.