How to build children of a nested form in the controller - ruby-on-rails

I know how to build the 2nd object in a controller but how do you build a third or a fourth?
In my case i need to build 3.
Location - has_many :product_dates, :products
ProductDate - has_many :products & belongs_to :location
Product - belongs_to :location, :product_date
I build the Location and Product Date easily:
def new
#location = Location.new
#location.product_dates.build
end
Now i need to build the products on the form. Can anyone show me how to do this?
EDIT: Complete Answer:
def new
#location = Location.new
product_date = #location.product_dates.build
product_date.products.build
end
<%= form_for #location do |f| %>
<%= f.text_field :business %>
<%= f.text_field :address %>
<%= f.fields_for :product_dates do |date| %>
<%= date.date_select :date %>
<%= date.fields_for :products do |product| %>
<%= product.text_field :name %>
<%= product.text_field :price %>
<%= product.text_field :tag_list %>
<% end %>
<% end %>
<%= f.submit "Create" %>
<% end %>

You'll learn everything in video here.
EDIT:
change the nested part with:
<%= f.fields_for :product_dates do |date| %>
<%= date.date_select :date %>
<%= date.fields_for :products do |product| %>
<%= product.text_field :name %>
<%= product.text_field :price %>
<%= product.text_field :tag_list %>
<% end %>
<% end %>
Because products are nested inside product_dates

add to your controller:
def new
#location = Location.new
#product_dates = #location.product_dates.build
#product = #product_dates.product.build
end
in you ProductDate model:
class ProductDate < ActiveRecord::Base
accepts_nested_attributes_for :products
...
in you Location model:
class Location < ActiveRecord::Base
accepts_nested_attributes_for :product_dates
...
And you form should be like this:
<% f.fields_for :product_dates do |date| %>
<%= date.text_field :content %>
<% date.fields_for :products do |product| %>
<%= product.text_field :content %>
<%end %>
<% end %>

Related

How to get multiple records for same model with Rails form_for

I am trying to build a basic recipes app but am having trouble allowing the user to input multiple ingredients for one recipe. The array of permitted params for the ingredients ends up empty. So I guess my question is - how do I permit the array of ingredients?
My controller:
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#ingredient = Ingredient.new
end
def create
safe_params = params.require(:recipe).permit(:title, :instruction, :category_id)
ingredient_params = params.require(:recipe).permit(:ingredient => [])
#recipe = Recipe.new(safe_params)
#recipe.save
ingredient_params.each do |i|
#recipe.ingredients << Ingredient.find_or_create_by(name: i[:ingredient][:name])
end
render body: YAML::dump(ingredient_params)
#redirect_to index_path(id: #recipe.id)
end
end
Form:
<%= form_for(#recipe, :url => create_path) do |f| %>
<%= f.label :category %>
<%= f.select :category_id, options_for_select(Category.all.map{|c|[c.title, c.id]}) %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for "ingredients[]", #ingredient do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Models:
class Recipe < ActiveRecord::Base
has_and_belongs_to_many :ingredients
accepts_nested_attributes_for :ingredients
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :recipes
end
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
end
There are several issues here, I'll just provide what I'd do:
#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#recipe.ingredients.new
end
def create
#recipe = Recipe.new safe_params
#recipe.save
end
private
def safe_params
params.require(:recipe).permit(:title, :instruction, :category_id, ingredients_attributes: [:name])
end
end
#app/views/recipes/new.html.erb
<%= form_for #recipe do |f| %>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
If you wanted to have multiple ingredients fields, you'll have to build multiple objects in the controller:
def new
#recipe = Recipe.new
3.times do
#recipe.ingedients.build
end
end
Everything else looks like it will work well.
--
As an extra, if you want to populate has_and_belongs_to_many relationships, you'll be able to just pass the [relationship]_ids parameter:
<%= form_for #recipe do |f| %>
<%= f.collection_select :ingredient_ids, Ingredient.all, :id, :name %>
<%= f.submit %>
<% end %>
This will only work for currently existing ingredients. If you want to create new ingredients, the above will work.
Need to make few changes to your code
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
# here you can decide how many ingredients do you want. (Not in form looping through text fields)
3.times do
ingredient = #recipe.ingredients.build
end
end
so fields for ingredients were get generated for three times.
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
Please go through following link, it will clear your idea about nested forms
http://railscasts.com/episodes/196-nested-model-form-part-1

unable to create or update a belongs_to record in Rails 4

I'm new to Rails and struggling to get my belongs_to association right. I have an app where a painting belongs to an artist and an artist can have_many paintings. I can create and edit my paintings, however I can not edit or create artists except through the console. Through much Googling I feel I have got myself turned around. Any help would be much appreciated!
Here's my routes.rb file:
MuseumApp::Application.routes.draw do
resources :paintings
resources :paintings do
resources :artists
resources :museums
end
root 'paintings#index'
end
Here's my paintings Controller
def show
#painting = Painting.find params[:id]
end
def new
#painting = Painting.new
##artist = Artist.new
end
def create
safe_painting_params = params.require(:painting).permit(:title, :image)
#painting = Painting.new safe_painting_params
if #painting.save
redirect_to #painting
else
render :new
end
end
def destroy
#painting = Painting.find(params[:id])
#painting.destroy
redirect_to action: :index
end
def edit
#painting = Painting.find(params[:id])
end
def update
#painting = Painting.find(params[:id])
if #painting.update_attributes(params[:painting].permit(:title, :image)) #safe_params
redirect_to #painting
else
render :edit
end
end
Here's the form in my paintings view:
<%= form_for(#painting) do |f| %>
<fieldset>
<legend>painting</legend>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :image %>
<%= f.text_field :image %>
</div>
<%= form_for([#painting,#painting.create_artist]) do |f| %>
<div>
<%= f.label :Artist %>
<%= f.text_field :name %>
</div>
</fieldset>
<%= f.submit %>
<% end %>
<% end %>
Artists Controller:
class ArtistsController < ApplicationController
def index
#artists = Artist.all
#artists = params[:q] ? Artist.search_for(params[:q]) : Artist.all
end
def show
#artist = Artist.find params[:id]
end
def new
#artist = Artist.new
end
def create
#painting = Painting.find(params[:painting_id])
#artist = #painting.create_artist(artist_params)
redirect_to painting_path(#painting)
end
def destroy
#artist = Artist.find(params[:id])
#Artist.destroy
redirect_to action: :index
end
def edit
#artist = Artist.find(params[:id])
end
def update
#painting = Painting.find(params[:painting_id])
#artist = #artist.update_attributes(artist_params)
redirect_to painting_path(#painting)
end
end
private
def artist_params
params.require(:artist).permit(:name)
end
Index view:
<h1> Hello and Welcome to Museum App</h1>
<h3><%= link_to "+ Add To Your Collection", new_painting_artist_path %></h3>
<%= form_tag '/', method: :get do %>
<%= search_field_tag :q, params[:q] %>
<%= submit_tag "Search" %>
<% end %>
<br>
<div id="paintings">
<ul>
<% #paintings.each do |painting| %>
<li><%= link_to painting.title, {action: :show, id:painting.id} %> by <%= painting.artist_name %></li>
<div id = "img">
<br><%= link_to (image_tag painting.image), painting.image %><br>
</div>
<%= link_to "Edit", edit_painting_path(id: painting.id) %>
||
<%= link_to 'Destroy', {action: :destroy, id: painting.id},method: :delete, data: {confirm: 'Are you sure?'} %>
<% end %>
</ul>
</div>
In your case you should use accepts_nested_attributes_for and fields_for to achieve this.
Artist
has_many :paintings, :dependent => :destroy
accepts_nested_attributes_for :paintings
Painting
belongs_to :artist
And also you should try creating artist with paintings like this
form_for(#artist) do |f| %>
<fieldset>
<legend>Artist</legend>
<%= f.label :Artist %>
<%= f.text_field :name %>
<%= fields_for :paintings, #artist.paintings do |artist_paintings| %>
<%= artist_paintings.label :title %>
<%= artist_paintings.text_field :title %>
<%= artist_paintings.label :image %>
<%= artsist_paintings.text_field :image %>
</fieldset>
<%= f.submit %>
<% end %>
Note:
You should be having your Artist Controller with at least new,create,edit and update methods defined in it to achieve this.
Edit
Try the reverse
Artist
has_many :paintings, :dependent => :destroy
Painting
belongs_to :artist
accepts_nested_attributes_for :paintings
form_for(#painting) do |f| %>
<fieldset>
<legend>Painting</legend>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :image %>
<%= f.text_field :image %>
<%= fields_for :artists, #painting.artists do |ff| %>
<%= ff.label :Artist %>
<%= ff.text_field :name %>
</fieldset>
<%= f.submit %>
<% end %>
Put this form in paintings views.

Can't create form_for associated models

I'm writing a simple web application which I like to call PMS (Projects Management System). It this app I have 2 models Projects and Students. I have set (I guess... cuz I'm a newbie) association between these two models. Projects has many students but student belongs to one project (Maybe it'll change with time).
But my problem is in getting everything work together. I don't know how I can insert new students inside new project form. I've tried everything and still nothing!
Here are my source files:
Projects controller:
class ProjectsController < ApplicationController
def show
#projects = Project.all
end
def create
#project = Project.new(project_params)
#project.status = "Waiting"
#project.save
redirect_to root_path
end
private
def project_params
params.require(:project).permit(:title, :lecturer)
end
end
Students Controller:
class StudentsController < ApplicationController
def create
#project = Project.find(params[:project_id])
#student = #project.students.create(params[:student])
#student.save
end
end
Models:
class Project < ActiveRecord::Base
has_many :students
end
class Student < ActiveRecord::Base
belongs_to :project
end
View:
Add new project
<%= form_for :project, url: projects_path do |f| %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :lecturer %>
<%= f.text_field :lecturer %>
</p>
<%= form_for([#project, #project.students.build]) do |s| %>
<p>
<%= s.label :name %><br />
<%= s.text_field :name %>
</p>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
Routes:
RoRPMS::Application.routes.draw do
# You can have the root of your site routed with "root"
root 'projects#show'
resources :projects do
resources :students
end
end
You can use "nested_form" to create project with students as well
<%= nested_form_for #project do |f| %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :lecturer %>
<%= f.text_field :lecturer %>
</p>
<%= fields_for :students do |s| %>
<p>
<%= s.label :name %><br />
<%= s.text_field :name %>
</p>
<% end %>
<%= f.link_to_add "Add new student", :students %>
<p>
<%= f.submit %>
</p>
<% end %>
In Project Model add
accepts_nested_attributes_for :students

Rails 4: fields_for in fields_for

I am learning RoR and i am trying to find how to set a fields_for in another one with has_one models like this:
class Child < ActiveRecord::Base
belongs_to :father
accepts_nested_attributes_for :father
end
class Father < ActiveRecord::Base
has_one :child
belongs_to :grandfather
accepts_nested_attributes_for :grandfather
end
class Grandfather < ActiveRecord::Base
has_one :father
end
I used Nested Model Form Part 1 on Railscasts to get these:
In children_controller.rb:
def new
#child = Child.new
father=#child.build_father
father.build_grandfather
end
def child_params
params.require(:child).permit(:name, father_attributes:[:name], grandfather_attributes:[:name])
end
And my form:
<%= form_for(#child) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
mother:<br>
<%= f.fields_for :father do |ff| %>
<%= ff.label :name %>
<%= ff.text_field :name %><br>
grand mother:<br>
<%= f.fields_for :grandfather do |fff| %>
<%= fff.label :name %>
<%= fff.text_field :name %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to retrieve the datas with:
<%= child.father.name %>
<%= child.father.grandfather.name %>
but the grandfather's name won't work.
I cannot find the mistake(s)...anyone to help on this?
Thanks!
Try switching:
<%= f.fields_for :grandfather do |fff| %>
to:
<%= ff.fields_for :grandfather do |fff| %>
And switching:
params.require(:child).permit(:name, father_attributes:[:name], grandfather_attributes:[:name])
To:
params.require(:child).permit(:name, father_attributes:[:name, grandfather_attributes:[:name]])

Rails 3 using fields_for in view doesn't work

I'm trying to implement master detail in a rails form with fields_for.
I have one model called Recipe:
class Recipe < ActiveRecord::Base
validates :name, :presence => true
validates :directions, :presence => true
has_many :recipe_ingredients
end
and one model called RecipeIngredient:
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
#belongs_to :ingredient
end
In the new controller I populate the ingredients with three empty records like this:
def new
#recipe = Recipe.new
3.times {#recipe.recipe_ingredients.build}
# #ingredients = RecipeIngredient.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #recipe }
end
end
What I would like to do in the view is to output the recipes fields (which works ok) and the three fields for recipe ingredients. In the top part of the view I have this:
<%= form_for :rec do |f| %>
I then list the recipe fields which are displayed correctly, like:
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
I then try to display the ingredient lines but the code never seems to go into the fields_for section:
first try:
<% for ingredient in #recipe.recipe_ingredients %>
This prints out three times
<% fields_for "...", ingredient do |ingredient_form| %>
But this never prints out
<p>
Ingredient: <%= ingredient_form.text_field :description %>
</p>
<% end %>
<% end%>
I have three empty lines in recipe_ingredients and the for loop seems to iterate three times but the code within fields_for never triggers.
Second try:
<% for recipe_ingredient in #recipe.recipe_ingredients %>
b
<% fields_for "...", recipe_ingredient do |recipe_ingredient| %>
Rec: <%= recipe_ingredient.text_field :description %>
<% end %>
<% end %>
Third try (based on the answer here):
<% form_for :recipe do |f| %>
<% f.fields_for ingredients do |ingredient_form| %>
<p>
Ingredient: <%= ingredient_form.text_field :description %>
</p>
<% end %>
<% end %>
Is there something obvious I'm doing wrong?
You should use <%= instead of <% when using form_for and fields_for
The correct code should look like this:
<%= form_for :recipe do |f| %>
<%= f.fields_for :ingredients do |ingredient_form| %>
<p>
Ingredient: <%= ingredient_form.text_field :description %>
</p>
<% end %>
<% end %>
Switch it up to
<% form_for :recipe do |f| %>
<% f.fields_for :ingredients do |ingredient_form| %>
<p>
Ingredient: <%= ingredient_form.text_field :description %>
</p>
<% end %>
<% end %>

Resources