I have a problem with has_many relationships setting up forms that do not update the values after submission.
Example
Upon submission of a new character, vn_id does not get updated and in Rails Consoles when I try to check for characters in a Vn, it returns empty.
I am trying to set up a form for characters which belongs to Vn which will be linked through the association but upon submission, it is not linked to Vn.
class Character < ActiveRecord::Base
validates :name, :presence => true
validates :summary, :presence => true
belongs_to :vn
end
class Vn < ActiveRecord::Base
has_many :characters
validates :name, presence: true
accepts_nested_attributes_for :characters
end
Form to create a new Character
<%= simple_form_for #character do |f| %>
<div class="col-lg-12">
<%= f.input :summary,input_html: {style: "height:150px;"} %>
</div>
<div class="col-lg-12">
<%= f.association :vn, as: :check_boxes %>
</div>
<%= f.button :submit , class: "btn btn-primary" %>
<% end %>
Controllers
class CharactersController < ApplicationController
def show
#character = Character.find(params[:id])
end
def new
#character = Character.new
end
def create
#character = Character.new(char_params)
if #character.save
else
render :action=>"new"
end
end
private
def char_params
params.require(:character).permit(:name, :summary,:voiceactor,:vn_name,vn_id: [])
end
end
class VnsController < ApplicationController
def show
#vn = Vn.find(params[:id])
end
def new
#vn = Vn.new
end
def create
#vn = Vn.new(vn_params)
if #vn.save
else
render :action=>"new"
end
end
private
def vn_params
def vn_params
params.require(:vn).permit(:name, :summary,:genre,:developer,:rating,vn_id: [])
end
end
end
Submission unpermitted vn_id
Parameters: {"utf8"=>"✓", "authenticity_token"=>"O2s6GVs77GGUMC5u3eZ9ebv/0l5u0MwP44yS8WGCQnjgwSgHfkbCmhEOUo6WKIMSMo5IfDuNYtMzyphnT/5cwQ==", "character"=>{"name"=>"2222", "voiceactor"=>"111", "summary"=>"one two tthee", "vn_id"=>"32"}, "commit"=>"Create Character"}
Unpermitted parameter: vn_id
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "characters" ("name", "summary", "voiceactor", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "2222"], ["summary", "one two tthee"], ["voiceactor", "111"], ["created_at", "2015-10-23 10:34:00.285447"], ["updated_at", "2015-10-23 10:34:00.285447"]
The problem is you're trying to permit an array for a singular association.
The f.association input is for your belongs_to association, so why would it allow multiple records?
You can even see how this works here:
The above are methods only for has_many
In short, they don't exist for belongs_to
Thus, when you call f.association :vn, you're populating an attribute vn_id which can then be associated in your database.
The only time you'd have vn_ids is if you used has_many etc.
This means...
def character_params
params.require(:character).permit(:vn_id)
end
with
f.association :vn
... should work
Somehow, changing vn_id: [] in CharactersController back to :vn_id worked.
Related
EDIT 3: Just to clarify, the goal and problem is to create 2 new records from the same form of which one is the parent and one is the child. The child needs the parent ID, but the parent is created from the same form that the child is.
EDIT 2: I think I'm getting closer. See log file at end, the deal is successfully saved and it looks like the client entry is starting to commit but then not saving. Code is updated below for changes.
I followed the Railscast #196 for nested forms and I am successfully able to edit, add and delete from nested forms as long as the record is already created. Now I am trying to use nested forms to create new records. I think I'm 99% of the way there, but I'm missing something I can't see anymore. I just need to figure out how to pass the id of the parent to the child.
In addition to the Railscast I used this answer to set inverse_of relationships and call the save order (using create instead of save though). I'm pretty sure the problem is in the form or the controller (but models are listed below too)
Nested Form (I tried to simplify to make it easier to read)
EDIT 2: remove hidden field
<%= form_for(#deal) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="deal-<%= #deal.id %>" >
<div class="form-group">
<%= f.label :headline %>
<%= f.text_field :headline, required: true, placeholder: "Headline" %>
</div>
<div class="form-group" id="clients">
<%= f.fields_for :clients do |client_form| %>
<div class="field">
<%= client_form.label :client %><br />
<%= client_form.text_field :name, placeholder: "Client name" %>
</div>
<% end %>
<%= link_to_add_fields "Add client", f, :clients %>
</div>
<div class="form-group">
<%= f.label :matter %>
<%= f.text_field :matter, placeholder: "Matter", rows: "4" %>
</div>
<div class="form-group">
<%= f.label :summary %>
<%= f.text_area :summary, placeholder: "Deal summary", rows: "4" %>
</div>
<div class="form-group">
<div class="action-area">
<%= f.submit "Add deal" %>
</div>
</div>
</div>
<% end %>
Controller
EDIT 2: include deal_id param & change save calls
class DealsController < ApplicationController
before_action :require_login
def new
#deal = Deal.new
#client = #deal.clients
end
def create
#deal = current_user.deals.create(deal_params)
if #deal.save
flash[:success] = "Your deal was created!"
redirect_to root_url
else
render 'deals/new'
end
end
private
def deal_params
params.require(:deal).permit(:headline, :matter, :summary, clients_attributes: [:id, :deal_id, :name, :_destroy])
end
end
EDIT 2: No longer yields errors in browser and success flash message is triggered
EDIT 2: Here is the console output on submit (the record is saved and can be viewed it just doesn't have a client)
Started POST "/deals" for ::1 at 2017-04-26 00:13:08 +0200
Processing by DealsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hbvpS6KsZOorR3u4LgNoG5WHgerok6j3yYzO+dFUHs9thsxRi+rbUkm88nb7A5WvlmWZEcvaDvCKywufP3340w==", "deal"=>{"headline"=>"headline", "client"=>{"name"=>"aaa"}, "matter"=>"", "summary"=>""}, "commit"=>"Add deal"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: client
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "deals" ("headline", "matter", "summary", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["headline", "headline"], ["matter", ""], ["summary", ""], ["user_id", 1], ["created_at", 2017-04-25 22:13:08 UTC], ["updated_at", 2017-04-25 22:13:08 UTC]]
(3.8ms) commit transaction
(0.1ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 16ms (ActiveRecord: 4.6ms)
Models for reference
Users
class User < ApplicationRecord
has_many :deals
end
Deals
class Deal < ApplicationRecord
belongs_to :user
has_many :clients, inverse_of: :deal
validates :headline, presence: true
accepts_nested_attributes_for :clients, allow_destroy: true
end
Clients
class Client < ApplicationRecord
belongs_to :deal, inverse_of: :clients
validates :name, presence: true
validates :deal_id, presence: true
end
You are missing the deal_id on the params
def deal_params
params.require(:deal).permit(:headline, :matter, :summary, clients_attributes: [:id, :deal_id, :name, :_destroy])
end
and on the create of deal, you can make something like this
def create
#deal = Deal.new(deal_params)
if #deal.save
...
and just add a hidden field on the form for the user_id parameter.
The problem is the validations in the client model. Removing the validations will allow the records to be correctly saved from the nested form.
In the Client model remove the name and deal_id validators
class Client < ApplicationRecord
belongs_to :deal, inverse_of: :clients
end
In the controller add build to the new action so it's nicer for the users:
def new
#deal = Deal.new
#client = #deal.clients.build
end
.build is not strictly necessary since the helper method that was created from the Rails Cast will create new client entries on demand, but with it the user is presented with the placeholder first entry which is ignored if blank.
To keep empty client records from saving I added a reject_if: proc to the deal model
class Deal < ApplicationRecord
belongs_to :user
has_many :clients, inverse_of: :deal
validates :headline, presence: true
accepts_nested_attributes_for :clients, allow_destroy: true, reject_if: proc { |attributes| attributes['name'].blank? }
end
I'll leave this open/unanswered in case someone can explain better why my solution worked and if there's a better way to do it.
Hi I am trying to implement a has_many :through nested form in my Rails 5 app to create a project with users assigned to it. However, when I try to create the project, I face the following validation errors:
- Project users user company must exist
- Project users user email can't be blank
- Project users user email is invalid
- Project users user password can't be blank
- Project users user firstname can't be blank
- Project users user lastname can't be blank
These are validations I did for the user model but I do not understand why they appear when I create a project with nested users. In addition, the console indicated :fullname as an unpermitted parameters, as shown below.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"3RtrzKKVrSOiGLYnHwubF3GaRAbcQE/61doRM5clT8GpFYeJoEVnU2lEmQsNwVO8qecdqig8xjwbTqqaqL1gYQ==", "project"=>{"name"=>"dsada", "description"=>"dadasd", "project_users_attributes"=>{"0"=>{"user_attributes"=>{"fullname"=>["", "5", "4", "3"]}}},"choice"=>"1", "button"=>"", "company_id"=>"1"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Company Load (0.3ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: fullname
Could anyone enlighten me on why I am facing these validation errors and why fullname is considered unpermitted although i included them in project strong params? My codes are as shown below. Thank you!
#Model - project.rb
class Project < ApplicationRecord
has_many :project_users, inverse_of: :project
has_many :users, through: :project_users
accepts_nested_attributes_for :project_users
end
#Model - project_user.rb
class ProjectUser < ApplicationRecord
belongs_to :project, inverse_of: :project_users
belongs_to :user, inverse_of: :project_users
accepts_nested_attributes_for :user
end
#Model - user.rb
class User < ApplicationRecord
has_many :project_users, inverse_of: :user
has_many :projects, through: :project_users
end
#Controller - projects_controller.rb
class ProjectsController < ApplicationController
def new
#company = Company.find(params[:company_id])
#project = #company.projects.build
#project.project_users.build.build_user
end
def create
#company = Company.find(params[:company_id])
#project = #company.projects.build(proj_params)
if #project.valid?
#project.save
render 'show'
else
render 'new'
end
end
private
def proj_params
params.require(:project).permit(:name, :description, project_users_attributes: [:id, user_attributes:[:fullname]])
end
end
end
#Views - new.html.erb
<%= form_for [#company, #project] do |f| %>
<%= f.label :title %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.fields_for :project_users do |project_user_form| %>
<%= project_user_form.fields_for :user do |user| %>
<%= user.label :team_members %>
<%= user.select :fullname, company.users.collect { |p| [ p.fullname, p.id ] }, {:prompt => 'Select Team Members'}, { :multiple => true} %>
<% end %>
<% end %>
<%= f.button :submit => "" %>
<% end %>
Here is why your strong parameters does not work well.
You're saying in proj_params that :fullname is a field. But it's not just a field - it's an array of values. So if you change that to :fullname => [] that'll permit the parameter.
But.. that doesn't make any sense.
Your code is trying to create new users, not assign them. That's why you get validations errors. The simplest way to prevent that is to pass IDs of user, which will try to update / build association, not create.
Then, you'd be better off to pass users' IDs from the view. Try passing user_ids instead of fullnames and don't forget user_ids => [] in strong parameters.
My goal is to create a new Order and an associated OrderItem using the same form.
Models
class Order < ActiveRecord::Base
belongs_to :user
has_many :order_items, dependent: :destroy
accepts_nested_attributes_for :order_items
validates_associated :order_items
end
class OrderItem < ActiveRecord::Base
belongs_to :order
default_scope -> { order(created_at: :desc) }
end
View
<% #items.each do |item| %>
<%= form_for(#order) do |f| %>
<%= f.hidden_field :user_id, :value => session[:user_id] %>
<%= f.fields_for :order_items do |oi| %>
<%= oi.hidden_field :product_id, :value => item.id %>
<%= oi.hidden_field :price, :value => item.price %>
<%= oi.number_field :quantity, value: 1, class: 'form-control', min: 1 %>
<% end %>
<%= f.submit "Buy Now", class: "btn btn-primary" %>
<% end %>
Controller
def new
#order = Order.new
#order.order_items.build
end
def create
#order = Order.new(order_params)
if #order.save
redirect_to cart_path
else
redirect_to root_url
end
end
private
def order_params
params.require(:order).permit(:user_id, :custom_item_id, order_items_attributes: [:product_id, :price, :quantity])
end
When submitting the nested form data to the database, the error message Unpermitted parameter: order_item gets returned and only the order data is saved.
Update <-- This is resolved
When I remove the "f." from <%= f.fields_for the form renders correctly and order_params includes the order_items data. This is interesting because the RailsGuide for Form Helpers includes the "f." http://guides.rubyonrails.org/form_helpers.html#nested-forms
Parameter
{"utf8"=>"✓", "authenticity_token"=>"<TOKEN>", "order"=>{"user_id"=>"1", "order_item"=>{"product_id"=>"5", "price"=>"120.0", "quantity"=>"1"}}, "commit"=>"Buy Now"}
The data still does not save to the corresponding models.
Update 2 <-- This is resolved
Updated the createaction in the controller to if #order.save!, below is the error message:
Validation failed: Order items order can't be blank, Order items product can't be blank, Order items quantity can't be blank, Order items price can't be blank, Order items is invalid
I believe that the mistake is in this line of code #order.order_items.build(order_params[:order_items_attributes]) but I am not sure what I need to change.
Update 3 Unpermitted parameter: order_item Error message
From the terminal:
Processing by OrdersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=><TOKEN>, "order"=> "user_id"=>"1", "order_item"=>{"product_id"=>"5", "price"=>"120.0", quantity"=>"1"}}, "commit"=>"Buy Now"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Unpermitted parameter: order_item
(0.2ms) begin transaction
SQL (1.4ms) INSERT INTO "orders" ("user_id", "created_at", "updated_at") VALUES (?, ?, ?) [["user_id", 1], ["created_at", "2016-03-18 14:58:21.724246"], ["updated_at", "2016-03-18 14:58:21.724246"]]
(14.4ms) commit transaction
The order_itemsdata does not get saved.
First, it seems like your association is incorrect in OrderItem. It should be belongs_to :order instead of belongs_to :order_item_params.
Second, I believe your form should say <%= f.fields_for :order_items do |oi| %> (:order_items not :order_item)
Finally, you should not need to do this in your controller: #order.order_items.build(order_params[:order_items_attributes])
Controller
def create
#order = Order.new(order_params)
if #order.save
redirect_to cart_path
else
redirect_to root#url
end
end
def order_params
params.require(:order).permit(:user_id, :custom_item_id, order_items_attributes: [:product_id, :price, :quantity])
end
The view that rendered the form was not in another controller and not in the orders controller. The show action of tht view did not include an instance variable for order_items. This caused the error messages. Thank you for all of your help. For anyone looking for resources for nested forms below are some helpful resources.
YouTube Video Instructions API dock for fields_for
Go Rails video for Form Nested Attributes and Fields For in Rails
Active Record Nested Attributes
Rails Guide: Active Record Associations
I have a form that includes two models, one of which is nested in another. When on the development server I submit the form, the submission generates the flash error message:
The form contains 1 error:
* Members organization can't be blank
Each Member belongs to 1 Organization. Might the error have to do with that the organization isn't saved yet when it's trying to save the Member (for a new organization, it's supposed to simulateneously save the organization and member)? Should I add code that sets organization_id for the member (but I would expect #organization.members.build to take care of that)? What am I doing wrong here?
The server log:
Processing by OrganizationsController#create as HTML
Parameters: {"utf8"=>"✓", "organization"=>{"org_name"=>"sadfsdaf", "phone"=>"sdfds", "members_attributes"=>{"0"=>{"username"=>"fsfdsfsad", "email"=>"sfdsdf#sfsdf.com", "fullname"=>"sdfds", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}}, "admin"=>"true", "commit"=>"Sign up"}
(0.5ms) begin transaction
Member Exists (0.4ms) SELECT 1 AS one FROM "members" WHERE LOWER("members"."email") = LOWER('sfdsdf#sfsdf.com') LIMIT 1
Member Exists (0.2ms) SELECT 1 AS one FROM "members" WHERE LOWER("members"."username") = LOWER('fsfdsfsad') LIMIT 1
Organization Exists (0.3ms) SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."org_name") = LOWER('sadfsdaf') LIMIT 1
Organization Exists (0.2ms) SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."phone") = LOWER('sdfds') LIMIT 1
(0.2ms) rollback transaction
Rendered shared/_error_messages.html.erb (1.9ms)
Organization model:
has_many :members, dependent: :destroy
accepts_nested_attributes_for :members, :reject_if => :all_blank, :allow_destroy => true
Member model:
belongs_to :organization
validates :organization_id, presence: true # There are other validations as well, but I don't think they matter for this question.
def send_activation_email
MemberMailer.account_activation(self).deliver_now
end
Organization controller:
def new
#organization = Organization.new
#member = #organization.members.build
end
def create
#organization = Organization.new(organizationnew_params)
if #organization.save
#member.send_activation_email # Method in Member model file.
flash[:success] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
private
def organizationnew_params
params.require(:organization).permit(:org_name,
:phone,
member_attributes: [:email,
:username,
:admin,
:password,
:password_confirmation
])
end
New view:
<%= form_for(#organization) do |f| %>
<%= f.text_field :org_name, %>
<%= f.text_field :phone %>
<%= f.fields_for :members do |p| %>
<%= p.text_field :username %>
<%= p.email_field :email %>
<%= p.password_field :password %>
<%= p.password_field :password_confirmation %>
<%= hidden_field_tag :admin, true %>
<% end %>
<%= f.submit "Sign up", class: "formbutton btn btn-default" %>
<% end %>
On the permitted params, try changing member_attributes to members_attributes. Also on the create action You are referring to #member but its not defined yet.
To debug further more replace your create action with:
def create
return render text: organizationnew_params
end
This way, when you send the form you will get all the permitted params and check if it's what you expected.
To Send the emails replace
#member.send_activation_email
With
#organization.members.each do |single_member|
single_member.send_activation_email
end
3rd Edit
Just use this
def create
#organization = Organization.new(organizationnew_params)
if #organization.save
#organization.members.each do |single_member|
single_member.send_activation_email
end
flash[:success] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
private
def organizationnew_params
params.require(:organization).permit(:org_name,
:phone,
members_attributes: [:id,:email,
:username,
:admin,
:password,
:password_confirmation
])
end
4th Edit
The validation:
When creating members with oraganization ( in the OraganizationsController ), you do not need to validate organization_id because it will be created at the same time of the organization. Unfortunately you will need the validation of the organization_id when adding a new member to the organization. So what we can do is add a condition to the validation:
#member.rb
attr_accessor :require_organization_id
belongs_to :organization
validates :organization_id, presence: true, if: :require_organization_id
How to use it:
member = Member.new(name: "Some Name")
member.valid? #true
member.require_organization_id = true
member.valid? #false
member.organization = Organization.first
member.valid? #true
If you set validation in member model i.e.
validates :organization_id, presence: true
then it looks in member params for organization_id which doesn't present in params of memeber
"members_attributes"=>{"0"=>{"email"=>"sfsa#sfsd.com", "username"=>"fsfdsfsad", "fullname"=>"sdfds", "password"=>"foobar", "password_confirmation"=>"foobar"}}
thats why its showing validation error so remove this line
validates :organization_id, presence: true
from your member model
Also for #member issue you need to do like this
#organization.memebers.each do |member|
member.send_activation_email
end
I've searched a lot and the common cause of this problem is attr_ascessible :model_attributes not being declared but I can't seem to get it working.
Looking at the Log below :referee, and :ticket_order values are in the params hash but then are inserted as null in the table. Foreign keys for user_id and event_id are saved in a new record without any errors. The warning about mass assignment led me to the attr_ascessible declaration, tried different variations of it without luck. I'm using devise.
Development log
Started POST "/events/1" for 127.0.0.1 at 2011-07-14 17:38:16 +0100
Processing by EventsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ddddddjTdnaLKQgZncSDGYt63JA=", "event"=>{"relationship"=>{"event_id"=>"1", "referee"=>"9", "ticket_order"=>"1"}}, "commit"=>"Confirm Purchase", "id"=>"1"}
User Load (20.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1
Event Load (13.1ms) SELECT "events".* FROM "events" WHERE "events"."id" = 1 LIMIT 1
AREL (18.7ms) INSERT INTO "relationships" ("user_id", "event_id", "created_at", "updated_at", "referee", "ticket_order") VALUES (8, 1, '2011-07-14 16:38:16.963351', '2011-07-14 16:38:16.963351', NULL, NULL)
WARNING: Can't mass-assign protected attributes: relationship
[paperclip] Saving attachments.
Redirected to http://localhost:3000/events/1
Completed 302 Found in 588ms
Events
class Event < ActiveRecord::Base
attr_accessible :artist, :venue, :show_info, :date, :doors, :ticket_issue, :ticket_price,
:travel_cost, :accomodation_cost, :hire_cost, :image, :avatar_url, :relationships_attributes, :referee, :ticket_order
has_many :relationships
has_many :users, :through => :relationships
accepts_nested_attributes_for :relationships
Events Controller
def new
#event = Event.new
#users = Relationships.find(:all)
relationship = #event.relationships.build()
end
def create
#event = Event.new(params[:event])
current_user.attend!(#event)
if #event.save
redirect_to #event, :notice => "Successfully created event."
else
render :action => 'new'
end
end
User.rb
def attending?(event)
relationships.find_by_event_id(event)
end
Relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :user
belongs_to :event
attr_accessible :event_id
form view
<%= semantic_form_for #event do |form| %>
<%= form.semantic_fields_for :relationship do |builder| %>
<%= builder.hidden_field :event_id, :value => #event.id %>
<%= builder.inputs do %>
<%= builder.input :referee, :as => :select, :collection => #event.users.all %>
<%= builder.input :ticket_order, :as => :number %>
<% end %>
<% end %>
Yes it must be this, replace:
form.semantic_fields_for :relationship
with:
form.semantic_fields_for :relationships