Related
I have tried a few methods and checked all of the existing SO questions I can find and can't find an answer to the problem. I'm a Rails newbie.
I have these models:
Project (has_many WeeklyReflections, has_many ProjectTasks)
WeeklyReflection (belongs_to Project)
ProjectTask (belongs_to Project)
Standard devise user model
My Project show page displays a list of ProjectTasks for the week. Using simplecalendar.
I can navigate forward and back a week at a time, which then changes the URL from www.site.com/projects/3 to www.site.com/projects/3?start_date=2108-05-30, or www.site.com/projects/3?start_date=2108-05-13, and this then shows all the tasks related to that week.
What I want to do as well, is for each page create a weekly journal entry, only one of which can be created for the week, however, I'm having problems saving it to my controller - I've tried a few ways and also tried putting it in as a hidden field on a form, and still can't get it to work.
I've put start_date:<%= render inline: params[:start_date].inspect %> on both the Project show page and form it returns a value correctly, i.e. "start_date:"2018-04-28"" or it returns nil if on the default show page which is to be expected.
The two methods I've tried: are - accessing the start_date param via the controller (in the controller below) and also adding the following to the form:
<%= hidden_field_tag(:weekending, params[:start_date]) %> (I've also tried using .to_date, as I think the string it returns isn't in the right format)
However, both methods are returning a nil value when the start_date param is returning 2018-04-28 on the view page.
Console log after create action:
Started POST "/projects/6/weekly_reflections" for 2.96.199.78 at 2018-05-07 16:20:14 +0000
Cannot render console from 2.96.199.78! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by WeeklyReflectionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"UFCXjIp1SZrql2JckHk1HrzDlzO2/WLVwcdvdh+FQKbdrVMfdaivtjS32oLaFwcFs0UcupVP+XV6VVnNwrM0XQ==", "week_reflection"=>{"reflectionentry"=>"adasda", "motivationscore"=>"1", "beliefscore"=>"1"}, "weekending"=>"2018-04-28", "commit"=>"Create Project reflection", "project_id"=>"6"}
Project Load (0.6ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.1ms) begin transaction
(0.1ms) commit transaction
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
(0.1ms) begin transaction
SQL (1.3ms) INSERT INTO "week_reflections" ("reflectionentry", "user_id", "project_id", "motivationscore", "beliefscore", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["reflectionentry", "adasda"], ["user_id", 2], ["project_id", 6], ["motivationscore", 1], ["beliefscore", 1], ["created_at", "2018-05-07 16:20:14.957131"], ["updated_at", "2018-05-07 16:20:14.957131"]]
(15.0ms) commit transaction
-
class WeeklyReflectionsController < ApplicationController
before_action :set_weekly_reflection, only: [:show, :edit, :update, :destroy]
before_action :set_project, only: [:new, :edit, :create, :show]
def index
#weekly_reflections= WeeklyReflection.all
end
def new
#weekly_reflection= WeeklyReflection.new
end
def create
#weekly_reflection= #project.weekly_reflections.create(project_reflection_params)
#weekly_reflection.user = current_user
#weekly_reflection.weekending = params[:start_date].to_date || Date.today.beginning_of_week
respond_to do |format|
if #weekly_reflections.save
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection was successfully created.' }
else
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection could not be added.' }
end
end
end
private
def weekly_reflection
#weekly_reflection= WeeklyReflection.find(params[:id])
end
def set_project
#project = Project.find(params[:project_id])
end
def weekly_reflection_params
params.require(:weekly_reflection).permit(:reflectionentry, :weekending, :motivationscore, :beliefscore)
end
end
Project show page:
<p id="notice"><%= notice %></p>
start_date:<%= render inline: params[:start_date].inspect %>
<div class="row well">
<div class="col-md-12">
<%= render 'project_detail' %>
</div>
<div class="col-md-12">
<% if #project.user == current_user && #project.active? %>
<br>
<%= render "project_tasks/task_modal" %>
<% end %>
</div>
<div class="col-md-12">
<%= render "project_reflection_calendar" %>
<% if #project.user == current_user && #project.active? %>
<h3>Add your thoughts for this week</h3>
<%= render "project_reflections/form_reflection" %>
<% end %>
</div
WeeklyEntry form, shown on the project show page:
<div class="row">
start_date:<%= render inline: params[:start_date].inspect %>
<%= form_for([#project, #project.weekly_reflections.build]) do |form| %>
<div class="col-md-12 projectform">
<%= form.label "Add your thoughts for this week" %>
<%= form.text_field :reflectionentry, :rows =>2, style: 'width:100%;' %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How motivated are you for your project this week?" %>
<%= form.select :motivationscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How strongly do you believe you will reach your project?" %>
<%= form.select :beliefscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<%= hidden_field_tag(:weekending, params[:start_date].to_date) %>
<div class="col-md-12 projectform">
<%= form.submit class:"btn btn-primary" %>
</div>
<% end %>
</div>
ApplicationRecord code:
class WeeklyReflection < ApplicationRecord
belongs_to :project
belongs_to :user
validates :reflectionentry, presence:true, length: {minimum:3, maximum: 540}
#required for simple_calendar
def start_time
self.weekending
end
end
class Project < ApplicationRecord
has_many :project_tasks, dependent: :destroy
belongs_to :user
has_many :weekly_reflections, dependent: :destroy
end
=> WeeklyReflection Schema:
(id: integer, reflectionentry: text, user_id: integer, project_id: integer, weekending: date, motivationscore: integer, beliefscore: integer, created_at: datetime, updated_at: datetime)
I think, this should work:
Add to your ProjectController show action:
#weekly_reflection= #project.weekly_reflections.new
Change in WeeklyReflectionsController:
def create
#weekly_reflection = #project.weekly_reflections.new(weekly_reflection_params)
#weekly_reflection.user = current_user
respond_to do |format|
if #weekly_reflections.save
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection was successfully created.' }
else
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection could not be added.' }
end
end
end
private
def weekly_reflection_params
params.require(:weekly_reflection).permit(:reflectionentry, :weekending, :motivationscore, :beliefscore)
end
Change in WeeklyReflection form:
<div class="row">
<%= form_for #weekly_reflection do |form| %>
<div class="col-md-12 projectform">
<%= form.label "Add your thoughts for this week" %>
<%= form.text_field :reflectionentry, :rows =>2, style: 'width:100%;' %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How motivated are you for your project this week?" %>
<%= form.select :motivationscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How strongly do you believe you will reach your project?" %>
<%= form.select :beliefscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<%= form.hidden_field :weekending, value: params[:start_date] || Date.today.beginning_of_week %>
<div class="col-md-12 projectform">
<%= form.submit class:"btn btn-primary" %>
</div>
<% end %>
</div>
Here we create #weekly_reflection variable inside the show action of ProjectsController, and just use it in form
That worked. Thanks so much.
With just a slight change to:
<%= form_for([#weekly_reflection.project, #weekly_reflection]) do |form| %>
I also had to add in:
<%= form.hidden_field :weekending, value: params[:start_date].try(:to_date).try(:beginning_of_week) || Date.today.beginning_of_week %>
So that I could capture the beginning of the week of the date in the param.
I have a World parent object and a State child object. I'm trying to create a new State object and rails isn't finding the world id. I'm trying to link to the new state form from the world show page, and the url shows .../worlds/1/states/new so why is this not picking up on the parent id? The error is supposedly coming from this line in the controller #world = World.find(params[:id]). I have tried using (params[:world_id]) even.
For brevity I'm only posting the relevant code here.
world.rb
class World < ApplicationRecord
belongs_to :user
has_many :states
end
state.rb
class State < ApplicationRecord
belongs_to :world
belongs_to :user
end
states_controller.rb
class StatesController < ApplicationController
before_action :set_state, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#states = State.all
end
def new
#world = World.find(params[:id])
#state = #world.states.build
end
def create
#world = World.find(params[:id])
#state = #world.states.build(state_params)
#state.user = current_user
respond_to do |format|
if #state.save
format.html { redirect_to #state, notice: 'State was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_state
#state = State.find(params[:id])
end
def state_params
params.require(:state).permit(:name, :summary, :history, :population, :inception, :life_expectancy, :land_mass,
:climate, :industry, :education, :mythology, :law, :culture, :world_id, :user_id)
end
end
The link to the new state form in worlds/show.html.erb:
<%= link_to 'New State', new_world_state_path(#world) %>
routes.rb
Rails.application.routes.draw do
resources :states
resources :worlds
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
root to: "home#index"
resources :users
resources :worlds do
resources :states
end
end
states/_form.html.erb
<div class="form">
<%= form_for(state) do |f| %>
<% if state.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(world.errors.count, "error") %> prohibited this state from being saved:</h2>
<ul>
<% state.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :name, placeholder: 'Name' %><br />
<fieldset>
<legend>Basic Info</legend>
<%= f.text_area :summary, placeholder: 'Summary About', rows: 6 %><br />
<%= f.text_area :history, placeholder: 'History', rows: 6 %><br />
<%= f.text_area :climate, placeholder: 'Climate', rows: 3 %><br />
<%= f.text_area :industry, placeholder: 'Industry', rows: 3 %><br />
<%= f.text_area :education, placeholder: 'Education', rows: 3 %><br />
<%= f.text_area :culture, placeholder: 'Culture', rows: 3 %><br />
<%= f.text_area :law, placeholder: 'Legal System, Crime & Punishment', rows: 3 %><br />
<%= f.text_area :mythology, placeholder: 'Mythology', rows: 3 %><br />
</fieldset>
<fieldset>
<legend>Quick Stats</legend>
<%= f.text_field :inception, placeholder: 'Inception' %><br />
<%= f.text_field :population, placeholder: 'Population' %><br />
<%= f.text_field :life_expectancy, placeholder: 'Ave. Life Expectance' %><br />
<%= f.text_field :land_mass, placeholder: 'Land Mass' %><br />
</fieldset>
<p><%= f.submit %></p>
<% end %>
</div>
rails console results when clicking 'New State' link
Started GET "/worlds/1/states/new" for 70.196.17.76 at 2017-05-22 13:43:47 +0000
Cannot render console from 70.196.17.76! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by StatesController#new as HTML
Parameters: {"world_id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
World Load (0.1ms) SELECT "worlds".* FROM "worlds" WHERE "worlds"."id" = ? LIMIT ? [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 3ms (ActiveRecord: 0.4ms)
ActiveRecord::RecordNotFound (Couldn't find World with 'id'=):
app/controllers/states_controller.rb:13:in `new'
Rendering /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (4.7ms)
Rendering /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.6ms)
Rendering /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.1ms)
Rendered /usr/local/rvm/gems/ruby-2.3.0/gems/actionpack-5.0.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (36.6ms)
Modify your link_to helper to specify and tell to Rails what's the parameter you're sending through it:
From:
<%= link_to 'New State', new_world_state_path(#world) %>
To:
<%= link_to 'New State', new_world_state_path(id: #world) %>
id because you're trying to find the World through :id as param.
Try also changing the param that's being received within the controller where you're setting the #world variable:
def new
#world = World.find(params[:world_id])
...
end
In the show.html.erb:
<%= link_to 'New World', new_world_state_path(world_id: #world) %>
Update: What we made:
In the app/views/worlds/show.html.erb to change the way the param was being setted:
From:
<%= link_to 'New Nation', new_world_state_path(world_id: #world_id) %> # #world_id wasn't defined
To:
<%= link_to 'New Nation', new_world_state_path(world_id: #world.id) %>
In the /app/views/states/_form.html.erb to add the world_id as a hidden_field:
<%= f.hidden_field :world_id, value: #world.id %>
And then in app/controllers/states_controller.rb to change the way the params were being received:
def new
#world = World.find(params[:world_id])
#state = #world.states.build
end
def create
#world = World.find(params[:state][:world_id])
...
The world_id while it is passed to the :new action, it may not be passed back on the form to the create action.
Your state_params are expecting a :world_id to be sent back so add a hidden field to send it back on the form.
new.html.erb
<%= f.hidden_field :world_id, :value => #world.id %>
and update the create action to
#world = World.find(params[:world_id])
My requirements
Basically I'm adding books to a database, and I want to store the Author in a separate table. So I have a table called authors which is referenced by the table books.
I want to create a rails form for adding a book, and I'd like it to be just a simple form for Author, Title, Publisher etc, and if it finds the author already in the authors table then it should just reference that record, and if it isn't in the authors table then it should add a new record and reference it.
while storing values i am getting in log file
Unpermitted parameters: author
Processing by BooksController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8sxeOVMVJucl5nN+kMpSaT1cB1yDPnk5gfKElWPiT5k=", "book"=>{"book_name"=>"life ", "price"=>"20", "author"=>{"author_name"=>"swamy", "title"=>"LIFE", "publisher"=>"cta"}}, "commit"=>"Submit"}
swamy
LIFE
cta
Author Load (1.6ms) SELECT "authors".* FROM "authors" WHERE "authors"."author_name" = 'swamy' AND "authors"."title" = 'LIFE' AND "authors"."publisher" = 'cta' LIMIT 1
(0.6ms) BEGIN
SQL (25.2ms) INSERT INTO "authors" ("author_name", "created_at", "publisher", "title", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["author_name", "swamy"], ["created_at", "2015-07-01 03:53:22.712401"], ["publisher", "cta"], ["title", "LIFE"], ["updated_at", "2015-07-01 03:53:22.712401"]]
(16.8ms) COMMIT
Unpermitted parameters: author
(0.7ms) BEGIN
SQL (1.0ms) INSERT INTO "books" ("Author_id", "book_name", "created_at", "price", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["Author_id", 8], ["book_name", "life "], ["created_at", "2015-07-01 03:53:22.865106"], ["price", 20], ["updated_at", "2015-07-01 03:53:22.865106"]]
(22.4ms) COMMIT
my code
books_Controller
class BooksController < ApplicationController
def new
end
def create
#a=params[:author_name]
puts #a
b=params[:book][:author][:author_name]
puts b
# #c = #b.authors
# #d = #c.author_name
c=params[:book][:author][:title]
d=params[:book][:author][:publisher]
puts c
puts d
#author = Author.find_or_create_by(author_name: b,title: c, publisher: d)
#book = Book.new(books_params)
#book.Author = #author
#book.save
redirect_to root_url
end
private
def books_params
params.require(:book).permit(:book_name, :price,:author_attributes => [:author_name, :title, :publisher])
end
end
model
class Author < ActiveRecord::Base
end
class Book < ActiveRecord::Base
belongs_to :Author
accepts_nested_attributes_for :Author
# def author_name=(author_name)
# self.author = Author.find_or_create_by_name author_name
#end
end
view
books.html.erb
<%= form_for Book.new do |f| %>
<p>
<%= f.label :book_name %><br />
<%= f.text_field :book_name %>
</p>
<p>
<%= f.label :price %><br />
<%= f.text_field :price %>
</p>
<%= f.fields_for :author do |b| %>
<p>
<%= b.label :author_name%><br />
<%= b.text_field :author_name %>
</p>
<p>
<%= b.label :title%><br />
<%= b.text_field :title %>
</p>
<p>
<%= b.label :publisher%><br />
<%= b.text_field :publisher%>
</p>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
~
Hi sir i am new to rails i know it looks simple but i unable it find solution
~
~
That is because your books_params has author_attributes when it should be author based on your parameters.
This should work:
def books_params
params.require(:book).permit(:book_name, :price, :author => [:author_name, :title, :publisher])
end
Your code is full of errors. Follow my changes.
#book.rb
class Book < ActiveRecord::Base
belongs_to :author
accepts_nested_attributes_for :author
end
#books_controller.rb
def new
#book = Book.new
#book.build_author
end
def create
#book = Book.new(books_params)
if #book.save
redirect_to root_url
end
end
Finally in your form change <%= form_for Book.new do |f| %> to <%= form_for #book do |f| %>
Change authore to author_attributes in view file, like this.
<%= f.fields_for :author_attributes do |b| %>
<p>
<%= b.label :author_name%><br />
<%= b.text_field :author_name %>
</p>
Also change respectively in controller also. I think, this much is enough.
def create
#book = Book.new(books_params)
#book.save
redirect_to root_url
end
I will Suggested you just debug your code
def books_params
byebug
params.require(:book).permit(:book_name, :price, :author => [:author_name, :title, :publisher])
end
1: check your requested params
2: kindly check params.require(:book)
Requested parameter would be like
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8sxeOVMVJucl5nN+kMpSaT1cB1yDPnk5gfKElWPiT5k=", "book"=>{"book_name"=>"life ", "price"=>"20", "author_"=>{"author_name"=>"swamy", "title"=>"LIFE", "publisher"=>"cta"}}, "commit"=>"Submit"}
check with this link NestedAttributes
Your controller would be this
class BooksController < ApplicationController
def new
end
def create
#author = Author.create(params[:book])
redirect_to root_url
end
private
def books_params
params.require(:book).permit(:book_name, :price,:author_attributes => [:author_name, :title, :publisher])
end
end
I created a simple application that has a Product and an Image model. Product has_many Images and Images has an attached file attribute (paperclip).
I created a simple_form for creating/editing Products and it works fine on creation. However, when editing a Product that has N images, rails inserts more N files - empty files.
I have set up a Simple Form custom input that tests if the image attachment exists in which case instead of rendering the builders input, it only renders an image_tag().
I see the html generated and it show something strange, a hidden tag:
<input id="product_images_attributes_0_id" name="product[images_attributes][0][id]" type="hidden" value="14">
And in the rails server console I see:
Processing by ProductsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"asdfasdfaasdf=", "product"=>{"reference"=>"Y1112CYL.E2", "images_attributes"=>{"0"=>{"id"=>"14"}}}, "commit"=>"Update Product", "id"=>"20"}
Product Load (0.6ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT 1 [["id", "20"]]
Unpermitted parameters: id
(0.2ms) BEGIN
SQL (1.0ms) INSERT INTO "images" ("created_at", "imageable_id", "imageable_type", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Tue, 18 Feb 2014 05:05:13 UTC +00:00], ["imageable_id", 20], ["imageable_type", "Product"], ["updated_at", Tue, 18 Feb 2014 05:05:13 UTC +00:00]]
(0.6ms) COMMIT
Here is the code to my implementation. If someone could help I would be very happy! Please pardon me if I left out any import part of the code, I will gladly edit the question to include it.
_form.html.erb
<%= simple_form_for(#product) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :reference %>
<h3>Images</h3>
<div id='images'>
<%= f.simple_fields_for :images do |image| %>
<%= render 'image_fields', :f => image %>
<% end %>
<div class='links'>
<%= link_to_add_association 'New image', f, :images %>
</div>
</div>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<%end%>
_image_fields.html.erb
<%= content_tag :div, class: "nested-fields images-fields" do %>
<%= content_tag :div, id: "new-image" do %>
<% if f.object.photo.exists? %>
<% f.template.image_tag(f.object.photo.url(:thumb)) %>
<% else %>
<% f.input :photo, :as => :photo %>
<% end %>
<% end %>
<% end %>
app/inputs/photo_input.erb
class PhotoInput < SimpleForm::Inputs::FileInput
def input
out = '' # the output string we're going to build
# check if there's an uploaded file (eg: edit mode or form not saved)
if object.send("#{attribute_name}?")
# append preview image to output
# <%= image_tag #user.avatar.url(:thumb), :class => 'thumbnail', id: 'avatar' %>
out << template.image_tag(object.send(attribute_name).url(:thumb), :class => 'thumbnail', id: 'photo')
else
# append file input. it will work accordingly with your simple_form wrappers
(out << #builder.file_field(attribute_name, input_html_options)).html_safe
end
end
end
ProductsController#update
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
For posterity, #omarvelous' comment seems to have solved it:
I added the reject if all blank to my accepts_nested_attributes_for
accepts_nested_attributes_for :images, :allow_destroy => true, :reject_if => :all_blank
Edit:
It turns out there is an issue with this solution. When reject_if => :all_blank is it stops the destroy from working properly. See this post that shows a workaround -- that I did not manage to get to work.
I am fairly new to Ruby on Rails and began with rails 4 right away.
I have sucessfully nested a Recipe and a Ingredient model so that they can be added in the same form. Next I want to nest quantity within ingredient so that that aswell can be added within the same form. Everything seems to be working fine up until when the quantity of the ingredient is about to be inserted in the database and from this i believe there is something wrong with the strong params in the recipes_controller. But i will post the full code below.
I am using simple_form for the forms.
Thankful for any help!
Here are my models:
class Recipe < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :ingredients, dependent: :destroy
accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
validates :title, presence: true
validates :desc, presence: true
end
class Ingredient < ActiveRecord::Base
belongs_to :recipe
has_many :quantities, dependent: :destroy
accepts_nested_attributes_for :quantities, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
class Quantity < ActiveRecord::Base
belongs_to :ingredient
end
Here is the recipes_controller
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
3.times do
ingredient = #recipe.ingredients.build
1.times {ingredient.quantities.build }
end
end
def create
#recipe = Recipe.new(params[:recipe].permit(:title, :desc, ingredients_attributes: [:id, :recipe_id, :name, :_destroy, quantities_attributes: [:id, :ingredient_id, :amount, :unit, :_destroy]]))
if #recipe.save
redirect_to #recipe
else
render "new"
end
end
def show
#recipe = Recipe.find(params[:id])
end
def edit
#recipe = Recipe.find(params[:id])
end
def update
#recipe = Recipe.find(params[:id])
if #recipe.update(params[:recipe].permit(:title, :desc))
redirect_to #recipe
else
render 'edit'
end
end
def destroy
#recipe = Recipe.find(params[:id])
#recipe.destroy
redirect_to recipes_path
end
def index
#recipes = Recipe.all
end
private
def post_params
params.require(:recipe).permit(:title, :desc, ingredients_attributes: [:id, :recipe_id, :name, :_destroy, quantities_attributes: [:id, :ingredient_id, :amount, :unit, :_destroy]])
end
end
Then i use simple form to create a form for recipe, ingredient and quantity through partials.
_form:
<%= simple_form_for #recipe do |f| %>
<%= f.error_notification %>
<%= f.input :title %>
<%= f.input :desc %>
<%= f.simple_fields_for :ingredients do |builder| %>
<%= render "ingredient_fields", :f => builder %>
<% end %>
<p class="links">
<%= link_to_add_association 'add ingredient', f, :ingredients %>
<p class="links">
<%= f.error :base%>
<%= f.submit %>
<% end %>
Which renders from _ingredients_fields:
<div class="nested-fields">
<%= f.input :name, label: "Ingredient" %>
<%= f.simple_fields_for :quantities do |builder| %>
<%= render "quantities_fields", :f => builder %>
<% end %>
<%= link_to_remove_association "remove", f %>
</div>
which renders from _quantities_fields: [EDITED]
<div class="nested-fields">
<%= f.input :amount %>
<%= f.input :unit %>
</div>
Trying to add new recipes result in the following log statement:
Started POST "/recipes" for 127.0.0.1 at 2013-10-29 14:15:40 +0100
Processing by RecipesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"t6LKgDLwAxaU9xo2ipyCM+j1yfVF9WrI8AoGTX+gRkw=", "recipe"=>{"title"=>"Pancakes", "desc"=>"Tasty", "ingredients_attributes"=>{"0"=>{"name"=>"Milk", "quantities_attributes"=>{"0"=>{"amount"=>"1", "unit"=>"Cup"}}, "_destroy"=>"false"}}}, "commit"=>"Create Recipe"}
[1m[35m (0.1ms)[0m begin transaction
[1m[36mSQL (3.5ms)[0m [1mINSERT INTO "recipes" ("created_at", "desc", "title", "updated_at") VALUES (?, ?, ?, ?)[0m [["created_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00], ["desc", "Tasty"], ["title", "Pancakes"], ["updated_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00]]
[1m[35mSQL (0.4ms)[0m INSERT INTO "ingredients" ("created_at", "name", "recipe_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00], ["name", "Milk"], ["recipe_id", 27], ["updated_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00]]
[1m[36m (7.8ms)[0m [1mcommit transaction[0m
Redirected to http://www.projectcookbook.dev/recipes/27
Completed 302 Found in 22ms (ActiveRecord: 11.7ms)
You're using similar render for _quantities and _ingredients partials, which is wrong. In _quantities_field you don't need
<%= f.simple_fields_for :quantities do |builder| %>
<%= render "quantities_fields", :f => builder %>
<% end %>
AND should adjust
<%= f.input :name, label: "Quantity" %>
in _quantities_fields.
UPD
I think the problem is in :reject_if-clause at Ingredient model. It should be
:reject_if => lambda { |a| a[:amount].blank? }
bc here you specify conditions for Quantity, not for Ingredient
On code styling:
1) In controller it's better to use relevant name of private method for strong parameters: recipe_params instead of post_params and then use it for creation of new Recipe #recipe = Recipe.new(recipe_params)
2) Current associations between Recipe, Ingredient and Quantity will lead to Ingredient duplication in case two Recipes use similar one. The reason is belongs_to, which define single association. Try another approach (bellow).
BTW. recently I've answered on the similar question. Check it out: How do I reference an existing instance of a model in a nested Rails form?
I thing you are missing in this part
<%= simple_form_for #recipe do |f| %>
it should be
<%= simple_nested_form_for #recipe do |f| %>