I'm having an inordinate amount of trouble using a nested model with fields_for in a form. Specifically, not all of the nested fields save. A User has many Experiences, but when I submit the form, an Experience with the correct user_id but NO CONTENT is inserted into the database.
Looking at the logs, I also get an error:
unpermitted parameters: experience.
Rails 4 nested attributes not saving doesn't help, unfortunately.
Here's the code:
SCHEMA
create_table "experiences", force: true do |t|
t.string "content", null: false, default: ""
t.integer "user_id"
end
MODEL
#user.rb
class User < ActiveRecord::Base
has_many :experiences
accepts_nested_attributes_for :experiences
#experience.rb
class Experience < ActiveRecord::Base
belongs_to :user
end
CONTROLLER
class UsersController < ApplicationController
def new
#user = User.new
#user.experiences.build
end
def update
#user = current_user
#user.experiences.build
#user.update!(user_params)
redirect_to root_path
end
def user_params
params.require(:user).permit(:username, :email, :password,
:password_confirmation, :title, :blurb, :city, :state,
:style_list, :experience_attributes => [:id, :content])
end
VIEW
<%= form_for #user do |f| %>
<!-- (Omitted) user fields -->
<%= f.fields_for :experience do |experience_fields| %>
<%= experience_fields.text_field :content, placeholder: 'Content' %>
<% end %>
<%= f.submit 'Edit profile' %>
<% end %>
Any help would be greatly appreciated!
Here's your problem:
#user.experiences.build # -> note "experience**s**"
This means when you use fields_for, you have to reference :experiences (you're currently referencing the singular):
<%= f.fields_for :experiences do |experience_fields| %>
<%= experience_fields.text_field :content, placeholder: 'Content' %>
<% end %>
This also goes for your strong_params:
params.require(:user).permit(experiences_attributes: [:id, :content])
Related
I am working on hand-rolling a user authentication system for fun. I've created two different models, a UserAccount and UserCredential the idea being that credentials such as email, username, profile pic, etc are separate from the actual account.
In order to create a UserCredential when a UserAccount is created I am using accepts_nested_attributes_for. I am running into some issues saving the UserCredential when the UserAccount saves.
Migration:
create_table :user_accounts, id: :uuid do |t|
t.references :user_credentials
...
end
add_reference :user_credentials, :user_account, type: :uuid, null: false, foreign_key: true
user_account.rb
has_one :user_credential, inverse_of: :user_account, dependent: :destroy
...
accepts_nested_attributes_for :user_credential
validates_associated :user_credential
user_credential.rb
class UserCredential < ApplicationRecord
validates :email, uniqueness: true
belongs_to :user_account, inverse_of: :user_credential
validates_presence_of :user_account
accounts_controller.rb
def new
#user = UserAccount.new
end
def create
#user = UserAccount.create!(user_params)
end
private
def user_params
params.require(:user_account).permit(user_credentials: [:email])
# I'm not sure if `user_credential` should be plural here or not?
end
_form.html.erb
<%= form_with model: #user, local: true do |form| %>
<%= form.fields_for :user_credentials do |u| %>
<div class="form-control">
<%= u.label :email %>
<%= u.email_field :email %>
</div>
<% end %>
...
<% end %>
Two small things I've noticed:
if I change the params and the fields_for to user_credential (removing the plural) the email field disappears.
If I keep the params as is I get this exception: ActiveModel::UnknownAttributeError (unknown attribute 'user_credentials' for UserAccount.):
I've seen people recommending adding a #user.user_credential.build() to the new method but doing that just gives me a nil:NilClass error.
I guess you need to change user_credentials to user_credentials_attributes
def user_params
params.require(:user_account).permit(user_credentials_attributes: [:email])
end
When nested attributes are submitted through a form, the _attributes tag is appended for the nested attributes parent.
I am building a nested form in ruby on rails.
The addition of a nested has_one association works fine. However, when I load the edit page, the foreign key company_id of the nested association is nullified.
I have tried update_only: true in accepts_nested_attributes_for and including :id in strong params as suggested in other similar answered questions on stackoverflow but nothing works for me.
Could anyone tell me what is actually causing the nested association to update and nullify its foreign key itself? My codes are as shown below. Thanks!
# company.rb
class Company < ApplicationRecord
has_one :mission
accepts_nested_attributes_for :mission, update_only: true
end
# mission.rb
class Mission < ApplicationRecord
belongs_to :company, optional: true
validates :description, presence: true, length: { maximum: 100 }
end
# companies_controller.rb
class CompaniesController < ApplicationController
def edit
#company = Company.find(params[:id])
#company.build_mission if #company.build_mission.nil?
end
def update
#company = Company.find(params[:id])
#company.assign_attributes(company_params)
if #company.valid?
#company.save
redirect_to companies_path
end
end
private
def company_params
params.require(:company).permit(mission_attributes: [:id, :description, :_destroy])
end
end
# edit.html.erb
<%= form_for #company, :url => company_path(#company), :html => {class: 'ui form', method: :put} do |f| %>
<%= f.fields_for :mission do |mission| %>
<div class="field">
<%= mission.label :mission %>
<%= mission.text_field :description %>
</div>
<% end %>
<%= f.button :submit => "", class: "ui button" %>
<% end %>
Hey I manage to solve the problem after a good sleep. Turns out i just have to play around with the if else condition at the companies controller level. The edit method should be amended to such:-
def edit
#company = Company.find(params[:id])
if #company.mission
else
#company.build_mission
end
end
Is there a way to update a field relative to a many-to-many associations via form_for?
Theses are my models:
class Grad < ActiveRecord::Base
belongs_to :school
has_many :grad_courses
has_many :courses, through: :grad_courses
accepts_nested_attributes_for :grad_courses, :courses
end
class Course < ActiveRecord::Base
belongs_to :school
has_many :grad_courses
has_many :grads, through: :grad_courses
end
class GradCourse < ActiveRecord::Base
belongs_to :grad
belongs_to :course
end
Here is the many-to-many migration with the extra property (semester):
create_table "grad_courses", force: :cascade do |t|
t.integer "grad_id"
t.integer "course_id"
t.integer "semester"
t.datetime "created_at"
t.datetime "updated_at"
end
What i'm trying to do is add and update the relations with the semester information dynamically with a form collection set (using form_for and chosen).
<%= form_for #grad, html: {class: "form-horizontal"} do |f| %>
...
<% (1..#grad.max).each do |semester| %>
<div class="form-group">
<%= f.label :course_ids, "#{semester}º semestre", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.collection_select :course_ids, #courses, :id, :name, {include_blank: true}, {multiple: true, :class=>'chosen-select form-control'} %>
</div>
</div>
<% end %>
...
<% end %>
This way, it is creating and updating the grad_courses, but without the semester.
I've tried lot of things, but didn't had success.
I'm able to manage the semester's info directly, like in this post: http://stackoverflow.com/questions/2168442/many-to-many-relationship-with-the-same-model-in-rails#=
I could pass an array with a hidden field, and manage the form manually, but it would take too much work, so I was wondering if there is an easy way to do this.
Something like :grad_course[:course_id][:semester] in :grad params? Is it possible?
the grad_course_controller:
def update
respond_to do |format|
if #grad.update(grad_params)
format.html { redirect_to #grad, notice: 'Cursos adicionados com sucesso' }
else
format.html { render :edit }
end
end
end
private
def set_grad
#grad = Grad.find(params[:id])
end
def set_courses
#courses = Course.where(school_id: #grad.school_id)
#gc = GradCourse.where(grad_id: #grad.id)
end
list through.
def grad_params
params.require(:grad).permit(:name, :min, :max, :school_id, course_ids: [] )
end
Update
I've been trying to solve this using Peter's advices, so I tried nested_attributes and fields_for. But I'm having problems with it too.
I need one collection_sellect for each semester (from 1 to Grad.max number), where the options will be the courses available for that semester (#courses), and for the selected ones a grad_course relation needs to be created or updated.
The problem with field_for is that one field is generated for every grad_course relation, and since each field is related with the grad_course.id I`m not able to add new ones properly.
Here is one of the tries i've made with fields for:
<%= f.fields_for :grad_courses do |builder| %>
<p>
<%= builder.text_field :semester %><br />
<%= builder.collection_select :course_id, #courses, :id, :name, {include_blank: true}, {multiple: true, :class=>'chosen-select form-control'} %>
</p>
<% end %>
with these changes in the grad_controller:
def grad_params
params.require(:grad).permit(:name, :min, :max, :school_id, course_ids: [], grad_courses_attributes: [:id, :course_id, :grad_id, :semester ])
end
Have you tried to add the :course_ids on the permit list?
Like this:
def grad_params
params.require(:grad).permit(:name, :min, :max, :school_id, :course_ids)
end
so I have looked at other instances of this error in other questions on SO and none seem to be helpful. So, my authentication system should allow a Business to sign up, and allow a user to sign up under their business. However, I'm getting a "couldn't find business without ID" error.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.references :company, foreign_key: true
t.timestamps
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.string :remember_digest
t.string :role
end
class CustomersController < ApplicationController
def new
set_business
#customer = #business.customers.create(user_params)
end
def create
#customer = Customer.new(customer_params)
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password_digest, :business_id)
end
def set_business
#business = Business.find (params[:business_id])
end
HTML snippet: Customer.new.html.erb
<h1>Sign Up</h1>
<%= form_for(#customer) do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name, :placeholder => "First name" %>
<%= f.label :last_name %>
<%= f.text_field :last_name, :placeholder => "Last name" %>
<%= f.label :email %>
<%= f.email_field :email, :placeholder => "Email" %>
<%= f.label :company_id %>
<%= f.collection_select :business_id, Business.all, :id, :name %>
<%= f.password_field :password_digest, :placeholder => "Password" %>
<%= f.submit "Create Account", class: "btn-submit" %>
<% end %>
class Business < ActiveRecord::Base
has_many :customers
end
class Customer < ActiveRecord::Base
belongs_to :business
end
How am I supposed to define the #business variable without getting this error so that a user can sign up under their business? I want them to select from a list of available companies on the form, which will then link them in the database when the user signs up. What am I doing wrong? I am very new to Ruby and I may need some good explanation to why this is happening.
thank you for your time :)
You should take a look at the contents of the params[], since there's no plain :business_id key, it's nested in a :customers hash:
change your set_business to somethink like this:
def set_business
#business = Business.find (params[:customer][:business_id])
end
EDIT: This will not work, since the first call to new method, the params is not supplied. You should only get a list of businesses to populate the form!
def new
#customer = Customer.new
#businesses = Business.all
end
EDIT2: In create method you don't need to set the business either, since it's already set by the form!
def create
#customer = Customer.create(customer_params)
#customer.save
end
Update the controller
customers_controller.rb
class CustomersController < ApplicationController
before_action :set_business
def new
#customer = #business.customers.build
end
def create
begin
#customer = #business.customers.new(customer_params)
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password_digest, :business_id)
end
def set_business
#business = Business.find (params[:business_id])
end
end
Simple one-to-many relationship going on and getting a Unpermitted parameter in the server log.
Cup 1----*> Contacts
The params looks like
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ay/wRwJTr4u5Vgu5oOrk7z4RC/OfeLJdN9WXuoyU7iQ=", "cup"=>{"name"=>"cupname", "location"=>"Somewhe", "type_ids"=>"1", "contact"=>{"first_name"=>"Greg", "last_name"=>"Ander", "email"=>"email#me.com", "telephone"=>"444906398"}}, "commit"=>"Create new cup"}
In cups_controller.rb
class CupsController < ApplicationController
def create
#cup = Cup.new(cup_params)
render nothing: true
end
private
def cup_params
params.require(:cup).permit(:name,:location,:type_ids,
:contacts_attributes => [:first_name, :last_name, :email, :telephone ])
end
In cup/new.html.erb - f is the main form for cup
<%= f.fields_for :contacts do |contact_info| %>
<%= contact_info.label :first_name %><%= contact_info.text_field :first_name %>
<%= contact_info.label :last_name %><%= contact_info.text_field :last_name %>
<%= contact_info.label :email %><%= contact_info.email_field :email %>
<%= contact_info.label :telephone %><%= contact_info.telephone_field :telephone %>
<% end %>
Keep getting Unpermitted parameter contact.
The Cup model has accepts_nested_attributes_for :contacts
Model files
class Cup < ActiveRecord::Base
has_many :contacts
has_and_belongs_to_many :types
accepts_nested_attributes_for :contacts
end
class Contact < ActiveRecord::Base
belongs_to :cup
end
Edit
Added updated cup_params and controller class. Also models.
You need to reference the contacts_attributes, not contact itself.
def cup_params
params.require(:cup).permit(:name,:location,:type_ids,
contacts_attributes: [:first_name, :last_name, :email, :telephone ])
end