Nested Forms error on unknown attribute Id in Rails 3 - ruby-on-rails

I'm attempting the nested forms based on Ryan Bates' #196 Railscasts Nested Forms 1. I'm using Nested_form and Simple_form gems. Maybe code is competing with gems?
The error that won't seem to go away is
ActiveRecord::UnknownAttributeError at /surveys/new
unknown attribute: customer_id
This occurs when I'm on the Survey page and submitting "New Survey".
I've got a Customer model and a Contact model which are associated to Survey.
It's referring to the last line in Survey Controller:
def new
12 #survey = Survey.new
13 3.times do
14 customer = #survey.customers.build
15 2.times { customer.contacts.build }
Solutions I tried include doing migrations adding the CustomerId to the Survey table or the Customer table. I've currently removed all references to customer_id.
Here's survey.rb
class Survey < ActiveRecord::Base
has_many :contacts
has_many :customers, :dependent => :destroy
accepts_nested_attributes_for :customers, :reject_if => lambda { |a| a[:content].blank? } :allow_destroy => true
attr_accessible :name, :customers_attributes
end
Here's customer.rb
class Customer < ActiveRecord::Base
belongs_to :survey
has_many :contacts
attr_accessible :company, :first_name, :last_name, :contacts_attributes, :customer_id
accepts_nested_attributes_for :contacts, :reject_if => lambda { |a| a[:content].blank? } :allow_destroy => true
end
here is contact.rb
class Contact < ActiveRecord::Base
attr_accessible :mobile_phone, :email
belongs_to :customer
end
and here's my form.html.haml
= simple_nested_form_for #survey do |f|
= f.error_notification
= f.input :name
= f.fields_for :customers do |builder|
= render "customer_fields", :f => builder
%p= f.submit "Submit"
Any help for a newbie? Thank you!!

Related

How to create an object if it does not exist in a nested form in Rails 6?

I need to create a Training, then add Attendances with nested form fields (cocoon) in a Rails 6 app. Here is the situation:
The models are the following:
Training model
class Training < ApplicationRecord
has_many :attendances, dependent: :destroy
has_many :people, through: :attendances
accepts_nested_attributes_for :attendances, reject_if: :all_blank, allow_destroy: true
end
Attendance model
class Attendance < ApplicationRecord
belongs_to :training
belongs_to :person
end
Person model
class Person < ApplicationRecord
has_many :attendances, dependent: :destroy
has_many :trainings, through: :attendances
end
If a Person exists in the database, then the user selects the person's name from a dropdown select field.
If the Person does not exist, then the user must enter the person's information manually by entering the name and age of the person.
How do I set up the controllers and form to allow a user to manually enter the person's name and age, and then automatically create that person and the attendance in the nested form fields when the training is saved?
The key is having an id field for the nested attributes. Rails's behavour is to update an existing record if the id is present, or to create a new record if the id is blank.
That, essentially, is all you need to do. Enter the person's name and age but leave the id empty, and a new record will be created.
I strongly recommend the cocoon gem https://github.com/nathanvda/cocoon which will give you a link_to_add_association helper for your training form that automatically lets you open a new, empty person form. When implemented you'll call the helper using something like this
<%= link_to_add_association 'add attendee', f, :people %>
You can ignore that the person is associated to the training by a join table... rails will create the join record automatically when you save the new person.
I used the link from the comment above: https://github.com/nathanvda/cocoon/wiki/A-guide-to-doing-nested-model-forms#the-look-up-or-create-belongs_to
And I used the last example in the wiki.
Here is how my final version of the MVC turned out for this feature:
The models
class Person < ApplicationRecord
has_many :attendances, dependent: :destroy
has_many :trainings, through: :attendances
end
class Attendance < ApplicationRecord
belongs_to :training
belongs_to :person
accepts_nested_attributes_for :person, :reject_if => :all_blank
end
class Training < ApplicationRecord
has_many :attendances
has_many :people, through: :attendances
accepts_nested_attributes_for :attendances, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :people
end
The trainings controller
class TrainingsController < ApplicationController
put your normal controller actions here and then in
training_params put this:
def training_params
params.require(:training).permit(
attendances_attributes: [:id, :_destroy, :person_id, person_attributes: [:id, :_destroy, :name, :age, :program_id]]
)
end
end
Add this to the trainings/_form
.row
.col.s12
.pt-5
%h6
Attendance
#people
= f.fields_for :attendances do |attendance|
= render 'attendance_fields', :f => attendance
.people_links
= link_to_add_association 'Add', f, :attendances
Add this to trainings/_attendance_fields
.nested-fields.attendance-person-fields
.field
.person_from_list
= f.select(:person_id, Person.all.map { |p| [p.name, p.id] }, { include_blank: true }, { :class => 'chosen-select' })
= link_to_add_association 'Or create', f, :person, data: {disable_with: "creating person"}
Finally in trainings/_person_fields
.nested-fields
.input-field
= f.text_field :name
= f.label :name
.input-field
= f.number_field :age
= f.label :age
I added coffee script :( in: person.coffee
$('#people').on('cocoon:before-insert', (e, attendance_to_be_added) ->
attendance_to_be_added.fadeIn 'slow'
return
)
$('#people a.add_fields').data('association-insertion-position', 'before').data 'association-insertion-node', 'this'
$('#people').on 'cocoon:after-insert', ->
$('.attendance-person-fields a.add_fields').data('association-insertion-position', 'before').data 'association-insertion-node', 'this'
$('.attendance-person-fields').on 'cocoon:after-insert', ->
$(this).children('.person_from_list').remove()
$(this).children('a.add_fields').hide()
return
return

Rails nested model form with has_one and has_many inside

I am using a nested form with has_one and a has_many relation inside it. The problem is that the data in the association is not being saved.
Below are the models, controller and the view I am using
Sell Order Model
class SellOrder < ActiveRecord::Base
belongs_to :company
has_one :work_order, dependent: :destroy
accepts_nested_attributes_for :work_order, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
Work Order Model
class WorkOrder < ActiveRecord::Base
belongs_to :sell_order
has_many :todos, dependent: :destroy
accepts_nested_attributes_for :todos, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
Todo Model
class Todo < ActiveRecord::Base
belongs_to :work_order
end
Sell Order Controller
class SellOrdersController < ApplicationController
def new
#sell_order = SellOrder.new(user: current_user)
1.times {
#work_order = #sell_order.build_work_order
#work_order.todos.build
}
end
def create
#sell_order = current_company.sell_orders.new(sell_order_params)
if #sell_order.save
redirect_to [:edit, #sell_order]
else
1.times {
#work_order = #sell_order.build_work_order
#work_order.todos.build
}
render :new
end
end
private
def sell_order_params
params.require(:sell_order).permit(:some_other_attributes, work_order_attributes: [todos_attributes: [:comment, :price]])
end
end
View
= f.fields_for :work_order do |builder_work_order|
= builder_work_order.fields_for :todos do |builder|
= builder.text_field :comment, class: 'form-control'
= builder.text_field :price, class: 'form-control'
When I submit the form, only the Sell Order is saved. No data goes in work order and todo tables. Does anyone has a solution for this type of situation?
Rails version: 4.1.1
Thanks in advance.

Formtastic Has Many Through Association

I have a Campaign model and a Category model. They have a has-many-through relationship with each other. The intermediate model is campaign_categories.
Campaign:
class Campaign < ActiveRecord::Base
attr_accessible :name, :carousel_image, :show_in_carousel, :title, :carousel_description,
:video_embed_code, :goal, :end_date, :backer_count, :author_name, :author_photo,
:author_description_md, :author_description_html, :description_markdown, :description_html,
:funds_description_md, :funds_description_html, :campaign_status_id
#associations
has_many :campaign_categories
has_many :categories, through: :campaign_categories
end
Category:
class Category < ActiveRecord::Base
attr_accessible :name
#associations
has_many :campaign_categories
has_many :campaigns, through: :campaign_categories
end
Campaign_Category:
class CampaignCategory < ActiveRecord::Base
attr_accessible :campaign_id, :category_id
belongs_to :campaign
belongs_to :category
end
I have following in campaigns.rb for Activeadmin:
ActiveAdmin.register Campaign do
form :html => { :enctype => 'multipart/form-data'} do |f|
f.inputs "Campaign Basic Information" do
f.input :name
f.input :categories
end
f.actions
end
end
The categories show up correctly in a multi select box. But I receive following error on form submission:
Can't mass-assign protected attributes: category_ids
I tried calling accepts_nested_attributes_for :categories in Campaign, but that did not work. How can I resolve this issue? Thanks.
Adding :category_ids to your attr_accessible call in the Campaign model should do it

Multiple nested form in a HABTM relation

I'm fighting with this bug for the past few hours and I can't make sense of it and my researches didn't give an answer.
It is a basic HABTM relationship. Inputs HABTM Visualizations, and I have a cross table InputsVisualizations that has some attributes of its own.
= form_for(#visualization) do |f|
= f.input :title
= f.fields_for :inputs_visualizations do |iv|
= iv.input :color
= iv.fields_for :input do |i|
= i.input :title
= f.button :submit, "Save"
class Input < ActiveRecord::Base
# Associations ------------------
has_many :inputs_visualizations, dependent: :destroy, order: "inputs_visualizations.order ASC"
has_many :visualizations, through: :inputs_visualizations
# Attributes --------------------
attr_accessible :title, :unit
end
class InputsVisualization < ActiveRecord::Base
# Associations ------------------
belongs_to :input
belongs_to :visualization
# Attributes --------------------
attr_accessible :input_id, :visualization_id, :color, :input_attributes
accepts_nested_attributes_for :input, :reject_if => lambda { |i| i[:title].blank? }, :allow_destroy => true
end
class Visualization < ActiveRecord::Base
# Associations ------------------
has_many :inputs_visualizations, dependent: :destroy, order: "inputs_visualizations.order ASC"
has_many :inputs, through: :inputs_visualizations, order: "inputs_visualizations.order ASC"
# Attributes --------------------
attr_accessible :title, :inputs_visualizations_attributes
accepts_nested_attributes_for :inputs_visualizations, :reject_if => lambda { |a| a[:input_id].blank? }, :allow_destroy => true
end
I need a form for Visualizations that let me manage both InputsVisualizations and Inputs. As you can see in my form, there are two nested fields_for.
Case 1:
I create a nested InputsVisualization with a nested Input (both are new_record). I save the form, they both are created. Cool!
Case 2:
From the same form, I update an Input (existing record). I save, nothing is updated even though the attributes are properly passed to the controller.
I read that nested_attributes don't work with belongs_to relationship, though it created it just fine. Why doesn't it update afterwards?
Thanks
The :reject_if condition on this line looks for an :input_id but that value is not included in the form. So this could prevent the update from going through.
accepts_nested_attributes_for :inputs_visualizations, :reject_if => lambda { |a| a[:input_id].blank? }, :allow_destroy => true

has_many through form and adding attribute to join table

This is my first stakoverflow question and fairly new to Rails. I've searched through previous similar questions but can't seem to figure this one out. I have a has_many through relationship with Users and Accounts and I have an extra boolean attribute "account_admin" on the UserAccount model (join table). I'm trying to figure out how to set this when I create a new Account.
User:
class User < ActiveRecord::Base
has_many :user_accounts, :dependent => :destroy
has_many :accounts, :through => :user_accounts
..
end
Account:
class Account < ActiveRecord::Base
has_many :user_accounts, :dependent => :destroy
has_many :users, :through => :user_accounts
end
UserAccount:
class UserAccount < ActiveRecord::Base
belongs_to :user
belongs_to :account
# includes account_admin boolean
end
I would like to be able to create an account, assign users to that account, and designate an account_admin all in one form. I have this so far which allows me to select the users in an account but I'm not sure how to set the account_admin boolean here as well.
= simple_form_for #account do |f|
= f.input :name
= f.input :description
= f.association :users, label_method: :to_label, :as => :check_boxes
= f.button :submit
Appreciate any tips. Thanks
Account model:
class Account < ActiveRecord::Base
attr_accessible :name, :description, :user_accounts_attributes
has_many :user_accounts, :dependent => :destroy
has_many :users, :through => :user_accounts
has_many :user_without_accounts, :class_name => 'User', :finder_sql => Proc.new {
%Q{
SELECT *
FROM users where id NOT IN (Select user_id from user_accounts where account_id = #{id})
}
}
accepts_nested_attributes_for :user_accounts, reject_if: proc { |attributes| attributes['user_id'] == '0' }
def without_accounts
new_record? ? User.all : user_without_accounts
end
end
In form:
= simple_form_for #account do |f|
= f.input :name
= f.input :description
- #account.user_accounts.each do |user_account|
= f.simple_fields_for :user_accounts, user_account do |assignment|
= assignment.check_box :user_id
= assignment.label :user_id, user_account.user.name rescue raise user_account.inspect
= assignment.input :account_admin
%hr
- #account.without_accounts.each do |user|
= f.simple_fields_for :user_accounts, #account.user_accounts.build do |assignment|
= assignment.check_box :user_id
= assignment.label :user_id, user.name
= assignment.input :account_admin
%hr
= f.button :submit

Resources