another mass assignment issue with nested attributes - ruby-on-rails

I have campaigns and messages. Campaigns have many messages and messages belongs to campaigns. I setup nested attributes for the models and in my view and i'm trying to create a message associated with the model. Here's the code:
class Campaign < ActiveRecord::Base
attr_accessible :message_id, :name, :group_id, :user_id, :messages_attributes
has_many :messages
belongs_to :group
belongs_to :user
accepts_nested_attributes_for :messages
end
class Message < ActiveRecord::Base
attr_accessible :body, :sent, :sent_at
belongs_to :user
belongs_to :campaign
has_many :responses
end
and the form:
= form_for #campaign, :html => {:class => 'form-horizontal'} do |f|
...removed error output code...
%legend
Enter the campaign information
.field
.control-group
%label.control-label
Campaign Name
.controls
= f.text_field :name
= f.collection_select(:group_id, current_user.groups.all, :id, :name, :include_blank => true)
= f.fields_for :message do |m|
= m.text_area :body, :rows => 3
.form-actions= f.submit "#{params[:action] == 'new' ? 'Create New Campaign' : 'Save Campaign'}", :class => 'btn btn-success'
I know it's probably something really simple but I keep getting a mass assignment issue with message. Here's the error:
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: message):
app/controllers/campaigns_controller.rb:18:in `update'
and finally the params that get created from the form:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"(removed)", "campaign"=>{"name"=>"Weekly Poker", "group_id"=>"1", "message"=>{"body"=>"s"}}, "commit"=>"Save Campaign", "id"=>"1"}

Because it's a has_many association it should be in plural form:
= f.fields_for :messages do |m|
Also, in the controller you will need:
def new
#campaign = Campaign.new
#campaign.messages.build
end

Related

ActiveRecord::AssociationTypeMismatch in RoastsController#create

This is a new error to me, and struggling to resolve it. It also states: Roaster(#70130698993440) expected, got "1" which is an instance of String(#70130675908140)
It's highlighting my create method in my Roasts Controller:
def create
#roast = Roast.new(roast_params)
The scenario is that I'm trying to create a triple nested form. for three models Roasts Countries and Regions where roasts has many countries and countries has many regions.
I'm assuming there is something wrong with the roast params, but I can see what it is. I have added the associations there for the nested models
def roast_params
params.require(:roast).permit(:roaster, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, :countries_attributes => [:country_name, :regions_attributes => [:region_name]])
end
my form
<div class="form-group">
<%= form.fields_for :countries do |countries_form| %>
<%= countries_form.label :country %>
<%= countries_form.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<% end %>
</div>
Roast Controller
...
def new
#roast = Roast.new
#roast.countries.build.regions.build
end
...
roast model
class Roast < ApplicationRecord
has_many :tastings
has_many :countries
has_many :notes, through: :tastings
has_many :comments, as: :commentable
belongs_to :roaster
accepts_nested_attributes_for :countries
country model
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roasts
region model
class Region < ApplicationRecord
belongs_to :country
I've nested the regions params in the country params, is that correct? I also saw on SO other issues with suggestions for setting config.cache_classes to true in development.rb but that didn't help here.
Update
So looking at this further, I believe it's not related to the nested forms, but rather a collection_select I'm using.
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster, Roaster.order(:roaster_name).all, :id, :roaster_name, prompt: true, class: "form-control") %>
So this select is pulling the roaster_name from a model called Roaster.
My params now look like the below:
params.require(:roast).permit(:roaster_name, :roaster, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, :countries_attributes => [:country_id, :country_name, :regions_attributes => [:region_id, :region_name]])
And looking at the console when submitting the form, it seems that just the :id of Roaster is getting passed, rather than the value of :roaster_name.
{"utf8"=>"✓",
"authenticity_token"=>"EG+zty85IiVsgipm1pjSAEZ7M66ELWefLq8Znux+cf89sSnVXxielRr1IaSS9+cJvdQD8g1D4+v2KqtKEwh6gw==",
"roast"=>{"roaster"=>"1", "name"=>"Espress", "countries_attributes"=>{"0"=>{"country_name"=>"UK"}}, "regions"=>{"region_name"=>"Highlands"}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""},
"commit"=>"Create Roast"}
Can't work this out
ActiveRecord::AssociationTypeMismatch is raised when an association-setter (Roast#roaster= in this case) is called with a value that is not an instance of the expected class. Roaster was expected, got String.
The issue seems to be with passing roaster in as a param, which is "1" (String) in your example. I'm guessing this is actually an ID of a Roaster, the form code in the question does not show it.
Perhaps you meant to permit and pass a roaster_id param?
def roast_params
params.require(:roast).permit(:roaster_id, # ...
end

nested_attributes with simple_field_for getting unpermitted parameters error rails

I have models
class Profile < ActiveRecord::Base
belongs_to :user
has_many :user_interests
has_many :interests, :through => :user_interests
accepts_nested_attributes_for :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :profile
belongs_to :interest
end
class Interest < ActiveRecord::Base
has_many :user_interests
has_many :profiles, :through => :user_interests
end
controller
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :date_of_birth, user_interests_attributes: {:interest => []})
end
view
=#"http://stackoverflow.com/questions/18428871/multi-column-forms-with-fieldsets"
= simple_form_for(#profile, html: {class: 'form-horizontal'}) do |f|
.well.well-lg
%fieldset
%legend Personal Information
.row
.col-sm-4
.form-group
= f.input :first_name, label: 'First Name*'
= f.hint 'No special characters, please!'
.col-sm-4
.form-group
= f.input :last_name, label: 'First Name*'
= f.hint 'No special characters, please!'
.row
.col-sm-4
.form-group
= f.input :date_of_birth, as: :string, 'data-behaviour'=>'datepicker'
%legend Other Information
.row
.col-sm-4
.form-group
= f.simple_fields_for :user_interests, UserInterest.new do |s|
= s.collection_select(:interest, Interest.all, :id, :name,{},{:multiple=>true})
= f.hint 'Please use Ctrl key on your keyboard to select multiple items'
.row
= f.submit
But getting error unpermitted parameters
profile_params
Unpermitted parameters: interest
=> {"first_name"=>"", "last_name"=>"", "date_of_birth"=>"", "user_interests_attributes"=>{"0"=>{}}}
where my params are:
params
=> {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"7/7sKljbi88cmUOen/WFWzhzV6exE8I8fBnNMA5EELw=",
"profile"=> {"first_name"=>"", "last_name"=>"",
"date_of_birth"=>"",
"user_interests_attributes"=>{"0"=>{"interest"=>"test"}}},
"commit"=>"Update Profile", "action"=>"update",
"controller"=>"profiles", "id"=>"1"}
Please correct me where I am wrong
You forgot to add accepts_nested_attributes_for :interest on UserInterest.
class Profile < ActiveRecord::Base
belongs_to :user
has_many :user_interests
has_many :interests, :through => :user_interests
accepts_nested_attributes_for :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :profile
belongs_to :interest
accepts_nested_attributes_for :interest
end
class Interest < ActiveRecord::Base
has_many :user_interests
has_many :profiles, :through => :user_interests
end
You might have to define interest as a valid parameter on strong_parameters definition in your controller.

Mass assignment error with polymorphic association

I'm getting a mass assignment error when submitting a nested form for a has_one polymorphic model. The form is trying to create Employee and Picture instances based on the polymorphic association Rails guide.
I would appreciate literally ANY functioning example of a nested creation form for a has_one polymorphic model! I know there are tons of questions on mass assignment errors but I've never seen a working example with polymorphic associations.
Models
class Picture < ActiveRecord::Base
belongs_to :illustrated, :polymorphic => true
attr_accessible :filename, :illustrated
end
class Employee < ActiveRecord::Base
has_one :picture, :as => :illustrated
accepts_nested_attributes_for :picture
attr_accessible :name, :illustrated_attribute
end
Migrations
create_table :pictures do |t|
t.string :filename
t.references :illustrated, polymorphic: true
end
create_table :employees do |t|
t.string :name
end
controllers/employees_controller.rb
...
def new
#employee = Employee.new
#employee.picture = Picture.new
end
def create
#employee = Employee.new(params[:employee])
#employee.save
end
...
Error
Can't mass-assign protected attributes: illustrated
app/controllers/employees_controller.rb:44:in `create'
{"utf8"=>"✓", "authenticity_token"=>"blah"
"employee"=>{"illustrated"=>{"filename"=>"johndoe.jpg"},
"name"=>"John Doe"},
"commit"=>"Create Employee"}
In the models, I've tried every permutation of :illustrated, :picture, :illustrated_attribute, :illustrated_attributes, :picture_attribute, :picture_attributes, etc. Any tips or examples?
EDIT:
_form.html.erb
<%= form_for(#employee) do |f| %>
<%= f.fields_for :illustrated do |form| %>
<%= form.text_field :filename %>
<% end %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
You need to specify the nested_attributes appropriately. accepts_nested_attributes_for takes the name of the association as parameter and then you need to add the same assoc_attributes to your attr_accessible. So change your Employee model to
class Employee < ActiveRecord::Base
has_one :picture, :as => :illustrated
accepts_nested_attributes_for :picture
attr_accessible :name, :picture_attribute
end
And change the field_for line in the view code to
<%= f.fields_for :picture do |form| %>

Accessing error messages for nested attribute field

I have a form created using the simple_form gem which populates 2 models using nested attributes. I want to check if there are any errors and display a new block. However, I'm not sure how to correctly access the error message for the location attribute of the Booking model.
class Booking < ActiveRecord::Base
belongs_to :customer
attr_accessible :date_wanted, :location
end
and
class Customer < ActiveRecord::Base
has_many :bookings
accepts_nested_attributes_for :bookings
attr_accessible :name, :phone, :bookings_attributes
validates_presence_of :name, :phone
end
Form view:
simple_form_for #customer, {:html => { :class => "form-horizontal" }} do |f|
= f.input :name
= f.input :phone
= f.simple_fields_for :bookings do |b|
= b.input :date
= b.input :location
- if #customer.errors[:appointments_attributes][:location]
# insert code if any validation errors for the date field were found
= f.button :submit
b is an instance of form builder, holding booking, so you can try:
# ...
if b.object.errors[:location]
# ...

Rails has_many :through and collection_select with multiple

I have the following problem using a has_many :through many-to-many relation in a multi-select via collection_select :multiple => true. I have Suppliers who deliver many Ingredients which can be delivered by many Suppliers. Have a look:
The Ingredient model:
class Ingredient < ActiveRecord::Base
has_many :ingredient_suppliers
accepts_nested_attributes_for :ingredient_suppliers, :allow_destroy => true
has_many :suppliers, :through => :ingredient_suppliers
end
The Supplier model:
class Supplier < ActiveRecord::Base
has_many :ingredient_suppliers
has_many :ingredients, :through => :ingredient_suppliers
end
The relationship Entity:
class IngredientSupplier < ActiveRecord::Base
belongs_to :ingredient
belongs_to :supplier
end
And this is the form. Note that I could not get it to work without specifying the :name:
<%= form_for(#ingredient) do |f| %>
<%= f.fields_for :suppliers do |supplier_fields| %>
<%= supplier_fields.collection_select (:supplier_ids,
Supplier.all(:order=>"name ASC"),
:id, :name,
{:selected => #ingredient.supplier_ids,
:include_blank => true},
{:multiple => true,
:name => 'ingredient[supplier_ids]'}) %>
<% end %>
<% end %>
If I remove the :name, then I get this error message:
Supplier(#-617951108) expected, got Array(#-608411888)
Request
Parameters:
{"commit"=>"Anlegen",
"authenticity_token"=>"MuEYtngwThharmM1KaAbH8JD3bScXiDwj0ALMytxl7U=",
"_method"=>"put",
"utf8"=>"✓",
"id"=>"1",
"ingredient"=>{"name"=>"Ingredient 1",
"nr"=>"00100",
"unit"=>"kg",
"mol_per_unit"=>"2000,
00000",
"description"=>"",
"suppliers"=>{"supplier_ids"=>["1",
"2"]}}}
The problem now is, that the PUT parameters only contain one supplier_id instead of an array of supplier_ids:
"ingredient"=>{"name"=>"Rohstoff 3", "nr"=>"00300", "unit"=>"Stk.", "mol_per_unit"=>"0,00000", "description"=>"", "supplier_ids"=>"2"}
I've got the problem solved. In this case, using fields_for was the error. The solution is using a collection_select, like this:
<%= collection_select(:ingredient, :supplier_ids,
Supplier.all(:order=>"name ASC"),
:id, :name, {:selected => #ingredient.supplier_ids, :include_blank => true}, {:multiple => true}) %>

Resources