So I have a deeply nested association among Polls, Questions, and Answers:
class Poll < ActiveRecord::Base
attr_accessible :description, :end_time, :start_time, :title, :user_id, :questions_attributes, :is_live
belongs_to :user
has_many :questions, :dependent => :destroy
# This is the plularized form of sms, it's not smss
has_many :sms, :through => :questions
has_many :feedbacks
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank?}, :allow_destroy => true
end
class Question < ActiveRecord::Base
attr_accessible :poll_id, :title, :answer_attributes
belongs_to :poll
has_many :answers, :dependent => :destroy
# THis is the plularized form of sms, it's not smss
has_many :sms
#If someone creates a form with a blank question field at the end, this will prevent it from being rendered on teh show page
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank?}, :allow_destroy => true
end
class Answer < ActiveRecord::Base
attr_accessible :is_correct, :question_id, :title
belongs_to :question
end
And am trying to save all objects in a single fields_for form like so.
= form_for #poll, :class=>'create-poll-form' do |f|
= f.text_field :title, :autofocus => true, :placeholder => "Poll Title"
= f.text_field :description, :placeholder => 'Description'
= f.fields_for :questions do |builder|
= render "questions/question_fields", :f => builder
= f.submit "Create Poll", :class => 'btn btn-danger'
Questions Partials
%p
= f.label :title, "Question"
= f.text_field :title
= f.check_box :_destroy
= f.label :_destroy, "Remove Question"
%p
= f.fields_for :answers do |builder|
= render "answers/answer_fields", :f => builder
Answers Partial
%p
= f.label :title, "Answer"
= f.text_field :title
= f.check_box :_destroy
= f.label :_destroy, "Remove Answer"
Yet the PollsController isn't persisting the data:
def create
binding.pry
#poll = current_user.polls.new(params[:poll])
# #poll = Poll.create(params[:poll])
binding.pry
#poll.save!
redirect_to root_path
end
def new
#poll = Poll.new
1.times do
question = #poll.questions.build
2.times {question.answers.build}
end
end
Any tips here would be amazing, I've been working on this for a bit and it's stumping me! Thanks in advance!
Also here's the server log:
Started POST "/polls" for 127.0.0.1 at 2013-06-06 20:14:47 -0400
Processing by PollsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hk+KuNsTLx3sH9pE7Mf8XETGsmxTsRN4/tWUBn3CIVE=", "poll"=>{"title"=>"Testing 1-2-1-2", "description"=>"Up on the mic", "questions_attributes"=>{"0"=>{"title"=>"Cake or Death?", "_destroy"=>"0", "answers_attributes"=>{"0"=>{"title"=>"Cake", "_destroy"=>"0"}, "1"=>{"title"=>"Death", "_destroy"=>"0"}}}}}, "commit"=>"Create Poll"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Question Load (0.5ms) SELECT "questions".* FROM "questions" WHERE "questions"."poll_id" IS NULL
(0.3ms) BEGIN
SQL (47.6ms) INSERT INTO "polls" ("created_at", "description", "end_time", "is_live", "start_time", "title", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id" [["created_at", Fri, 07 Jun 2013 00:16:46 UTC +00:00], ["description", "Up on the mic"], ["end_time", nil], ["is_live", nil], ["start_time", nil], ["title", "Testing 1-2-1-2"], ["updated_at", Fri, 07 Jun 2013 00:16:46 UTC +00:00], ["user_id", 1]]
(0.8ms) COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 170668ms (ActiveRecord: 54.9ms)
I'm not sure if this is the problem but in your
class Question < ActiveRecord::Base
attr_accessible :poll_id, :title, :answer_attributes
but in your log file it has
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hk+KuNsTLx3sH9pE7Mf8XETGsmxTsRN4/tWUBn3CIVE=", "poll"=>{"title"=>"Testing 1-2-1-2", "description"=>"Up on the mic", "questions_attributes"=>{"0"=>{"title"=>"Cake or Death?", "_destroy"=>"0", "answers_attributes"=>{"0"=>{"title"=>"Cake", "_destroy"=>"0"}, "1"=>{"title"=>"Death", "_destroy"=>"0"}}}}}, "commit"=>"Create Poll"}
The difference being :answers_attributes, rather than :answer_attributes i.e. answer vs answer*s
This may be causing the data to be thrown away.
Related
I'd like users to be able to create a User System with multiple existing Parts. That it. Currently when I edit or create a user system with a part select I get.
Processing by UserSystemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LgudsjWoAROXi0DGpUaH0ie2GNAuooRGtsPN+fOg+Gtev6Q6QmJC4dYIwFW9evaBOa0TUWotrQec1o1ROCbwFQ==", "user_system"=>{"name"=>"adfa"}, "parts_user_systems"=>{"part_id"=>["", "2", "3"]}, "button"=>""}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 101]]
(0.1ms) BEGIN
SQL (0.6ms) INSERT INTO "user_systems" ("name", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "adfa"], ["user_id", 101], ["created_at", "2015-07-10 17:10:39.564555"], ["updated_at", "2015-07-10 17:10:39.564555"]]
(0.2ms) COMMIT
Nothing get's submitted to the join table.
class UserSystem < ActiveRecord::Base
has_many :parts_user_systems, foreign_key: "part_id"
has_many :parts, through: :parts_user_systems, foreign_key: "part_id"
end
class Part < ActiveRecord::Base
has_many :parts_user_systems
has_many :user_systems, through: :parts_user_systems
end
class PartsUserSystem < ActiveRecord::Base
belongs_to :part
belongs_to :user_system
end
class UserSystemsController < ApplicationController
def user_system_params
params.require(:user_system).permit(:name, :user_id, part_ids: [:ids])
end
end
_form.html.erb
<%= form_for(#user_system) do |f| %>
<div class="field">
<%= f.label :part_id, "Part " %><br/>
<%= collection_select :parts_user_systems, :part_id, Part.all, :id, :name, {:selected => 1}, {:multiple => true} %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
</div>
<% end %>
show.html.erb
<%= #user_system.parts.each do |part| %>
<li><%= part.name%> by by <%= part.parts_user_system%></li></li>
<% end %>
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| %>
My problem is that a tag wont be created which is nested in posts.
I have Post which have tags nested in them. Like this
resources :posts do
resources :tags, :only => [:new, :create, :destroy]
end
In my tags controller i have this
class TagsController < ApplicationController
before_filter :authenticate_user!
def new
#post = Post.find(params[:post_id])
#tag = Tag.new
end
def create
#post = Post.find(params[:post_id])
#tag = #post.tags.build(params[:tags])
if #tag.save
flash[:notice] = "Tag created"
redirect_to #tag.post
else
flash[:error] = "Could not add tag at this time"
redirect_to #tag.post
end
end
end
new.html.erb
<%= form_for [#post, #tag] do |f| %>
<% if #tag.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#tag.errors.count, "error") %> prohibited this tag from being saved:</h2>
<ul>
<% #tag.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.text_field :tagable_type, :placeholder => "Type" %>
</div>
<div class="field">
<%= f.text_field :tagable_id, :placeholder => "Id" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Tag model
class Tag < ActiveRecord::Base
attr_accessible :post_id, :user_id, :tagable_type, :tagable_id
validates :post_id, presence: true
belongs_to :tagable, :polymorphic => true
belongs_to :post
belongs_to :user
end
Post model
class Post < ActiveRecord::Base
attr_accessible :body, :link, :thumbnail, :title, :user_id, :youtube, :youtube_id, :website_name, :image_field
default_scope order: 'posts.active DESC'
has_many :tags, :dependent => :destroy
has_many :comments, :as => :commentable, :dependent => :destroy
belongs_to :user
end
UPDATE:
Now i can create a tag but the tagable_type and tagable_id fields are not being created for some reaseon
Started POST "/posts/18/tags" for 127.0.0.1 at 2013-06-26 15:54:14 +0200
Processing by TagsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"gwpTs0Qqcrre4tH974RrfpaENGZKtSbkJx2U0H67AcM=", "tag"=>{"tagable_type"=>"Player", "tagable_id"=>"2"}, "commit"=>"Create Tag", "post_id"=>"18"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? ORDER BY posts.active DESC LIMIT 1 [["id", "18"]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "tags" ("created_at", "post_id", "tagable_id", "tagable_type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Wed, 26 Jun 2013 13:54:14 UTC +00:00], ["post_id", 18], ["tagable_id", nil], ["tagable_type", nil], ["updated_at", Wed, 26 Jun 2013 13:54:14 UTC +00:00], ["user_id", 1]]
(6.9ms) commit transaction
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 18 ORDER BY posts.active DESC LIMIT 1
Redirected to http://localhost:3000/posts/18
Completed 302 Found in 15ms (ActiveRecord: 7.9ms)
There is a typo in the create() action of your TagsController:
#tag = #post.tags.build(params[:tags])
The hash used to create the tag should be params[:tag], not params[:tags].
Noob question, I'm sure, but I can't seem to find my mistake. SymptomSets are saving w/ the proper user_id, but the nested symptoms disappear. Note that the user model structure is identical to that in the Rails Tutorial (save that it has_many :symptom_sets)
Models:
class SymptomSet < ActiveRecord::Base
attr_accessible :symptoms, :symptoms_attributes
belongs_to :user
has_many :symptoms, :dependent => :destroy
accepts_nested_attributes_for :symptoms, allow_destroy: true
end
class Symptom < ActiveRecord::Base
attr_accessible :name, :duration, :symptom_set_id
belongs_to :symptom_set
end
Controller:
class SymptomSetsController < ApplicationController
before_filter :signed_in_user, only: [:create, :new]
def new
#symptom_set = SymptomSet.new
3.times do
symptom = #symptom_set.symptoms.build
end
end
def create
#symptom_set = current_user.symptom_sets.build(params[:symptom_sets])
if #symptom_set.save
flash[:success] = "Symptoms submitted!"
redirect_to root_url
else
render 'static_pages/home'
end
end
And the View:
<%= simple_form_for #symptom_set, :html => { :class => 'form-inline' } do |f| %>
<%= f.fields_for :symptoms do |builder| %>
<%= render 'symptom_fields', f: builder %>
<% end %>
<div class="actions"><%= f.submit %></div>
<% end %>
And the partial:
<%= f.input :name,
:collection=> ["Cough", "Fever", "Headache", "Lethargy"],
label: "Symptom",
prompt: "Select a symptom",
:input_html => { :class => "span3" }%>
<%= f.input :duration,
:collection => 1..14,
label: "Duration",
prompt: "How many days?" %>
finally, the rails server console outputs the following:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"s7ksuk40M2r76Nq4PGEEpTpkCECxFniP4TtpfSHszQk=", "symptom_set"=>{"symptoms_attributes"=>{"0"=>{"name"=>"Cough", "_destroy"=>"false", "duration"=>"2"}, "1
"=>{"name"=>"Fever", "_destroy"=>"false", "duration"=>"2"}, "2"=>{"name"=>"", "_destroy"=>"1", "duration"=>""}}}, "commit"=>"Create Symptom set"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" ='OH6_nuvySNjd6AbTuDunsw' LIMIT 1
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "symptom_sets" ("created_at", "updated_at", "user_id") VALUES ($1, $2, $3)
RETURNING "id" [["created_at", Tue, 05 Feb 2013 21:12:07 UTC +00:00], ["updated_at", Tue, 05 Feb 20
13 21:12:07 UTC +00:00], ["user_id", 1]]
(1.1ms) COMMIT
I'd try changing:
#symptom_set = current_user.symptom_sets.build(params[:symptom_sets])
to:
#symptom_set = current_user.symptom_sets.new(params[:symptom_sets])
I don't know if build would work there.
And also checking the params on the terminal log if it is called symptom_sets and if it's sending the parameters of nested form attributes.
EDIT:
I Really think your param's name would be symptom_set in singular. check that.
Using Ryan Bate's RailsCasts #124 Beta Invites (as well as the updated rails 3.1 api) as a crutch, I'm trying to put together my first piece of Action Mailer functionality: inviting someone to collaborate with you on a project.
My issue is that the :recipient_email isn't getting saved in the DB and I can't see what I'm missing.
config/initializers/setup_mail.rb
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => 'blah.com',
:user_name => 'gmail username',
:password => 'gmail password',
:authentication => 'plain',
:enable_starttls_auto => true
}
ActionMailer::Base.register_interceptor(DevelopmentMailInterceptor) if Rails.env.development?
app/models/invitation.rb
class Invitation < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
attr_accessor :recipient_email
belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
has_one :recipient, :class_name => "User", :foreign_key => "recipient_id"
validates_presence_of :recipient_email, :on => :create, :message => "can't be blank"
validates :recipient_email, :format => email_regex
validate :recipient_is_not_registered
before_create :generate_token
def sender_name
sender.user_name
end
def sender_email
sender.email
end
private
def recipient_is_not_registered
exsisting_user = User.find_by_email(recipient_email)
if exsisting_user
errors.add :recipient_email, 'is already a member.'
else
recipient_email
end
end
def generate_token
self.token = Digest::SHA1::hexdigest([Time.now, rand].join)
end
end
app/models/user.rb (minus all the auth stuff)
class User < ActiveRecord::Base
attr_accessible :invitation_token
has_many :sent_invitations, :class_name => "Invitation", :foreign_key => "sender_id"
belongs_to :invitation
end
app/controller/invitations_controller.rb
class InvitationsController < ApplicationController
before_filter :authenticate
def new
#title = "Invite client"
#invitation = current_user.sent_invitations.new
end
def create
#invitation = current_user.sent_invitations.create!(params[:invitation])
sender_name = #invitation.sender_name
sender_email = #invitation.sender_email
if #invitation.save
Mailer.invitation(#invitation, signup_url(#invitation.token), sender_name, sender_email).deliver
flash[:success] = "Your client has been sent the email. Why not create a booking for them?"
redirect_to bookings_path
else
#title = "Invite client"
render :new
end
end
end
app/mailers/mailer.rb
def invitation(invitation, signup_url, sender_name, sender_email)
#signup_url = signup_url
#sender_name = sender_name
#sender_email = sender_email
mail(:to => invitation.recipient_email,
:subject => "Invitation to Join",
:from => #sender_email)
end
app/views/invitations/_invitation_form.html.erb
<%= form_for #invitation do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= f.hidden_field :email_token %>
<br />
<div class="field">
<%= f.label :recipient_email, "Client's email address" %>
<%= f.email_field :recipient_email %>
</div>
<br />
<div class="action">
<%= f.submit "Send invitation", :class => "a small white button radius" %>
</div>
<% end %>
The SQL log showing that the :recipient_email isn't getting saved
Started POST "/invitations" for 127.0.0.1 at 2011-12-14 21:27:11 +1100
Processing by InvitationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7/SGZypGXtf9ShlcjC6o8ZRj2Qe4OJTHdjis2/m3ulc=", "invitation"=>{"recipient_email"=>"users#email.com"}, "commit"=>"Send invitation"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.1ms) BEGIN
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'users#email.com' LIMIT 1
SQL (0.4ms) INSERT INTO "invitations" ("created_at", "recipient_email", "sender_id", "sent_at", "token", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Wed, 14 Dec 2011 10:27:11 UTC +00:00], ["recipient_email", nil], ["sender_id", 1], ["sent_at", nil], ["token", "56fba1647d40b53090dd49964bfdf060228ecb2d"], ["updated_at", Wed, 14 Dec 2011 10:27:11 UTC +00:00]]
(10.2ms) COMMIT
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.1ms) BEGIN
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'users#email.com' LIMIT 1
(0.1ms) COMMIT
Rendered mailer/invitation.text.erb (0.4ms)
Sent mail to users#email.com (7ms)
Date: Wed, 14 Dec 2011 21:27:11 +1100
From: admin#email.com
It's probably the attr_accessor :recipient_email line in your Invitation model. Take that line out as recipient_email is a database field, ain't it?