I've used the nested_form gem, whenever i try and submit something to my form i get the Can't mass-assign protected attributes:items message, even though i've already put attr_accessible in my models.
Form:
<%= nested_form_for(#goods_in) do |f| %>
...
<%= f.fields_for :items do |i| %>
<td><%= i.text_field :description, :autocomplete => :off%></td>
<td><%= i.text_field :quantity, :autocomplete => :off %></td>
<th><%= i.link_to_remove "Remove this item" %></th>
<% end %>
<%= f.submit :"Submit Delivery" %>
<% end %>
Goods In Model:class GoodsIn < ActiveRecord::Base
belongs_to :supplier
has_many :items
attr_accessible :c4lpono,
:courier,
:deliverydate,
:deliverynoteno,
:destination,
:notes,
:quantity,
:signedby,
:supplier_id,
:partcode_ids
accepts_nested_attributes_for :supplier
validates :c4lpono,
:deliverydate,
:deliverynoteno,
:destination,
:quantity,
:signedby,
:presence =>true
end
Item Model
class Item < ActiveRecord::Base
belongs_to :goods_in
attr_accessible :quantity,
:partcode,
:description,
:goods_in_id
accepts_nested_attributes_for :goods_in
end
Goods In Controller:
def create
#goods_in = GoodsIn.new(params[:goods_in])
end
you have to add
attr_accessible :items_attributes
And here's a link to the documentation :)
I think there's an error in your Goods model.
It should read has_many :items instead of has_many :item.
It's hard to tell what you are trying to achieve with your models but I think you want the folllowing:
Your GoodsIn needs to have the relationship for accepts_nested_attributes_for :items . The belongs_to and accepts_nested_attributes_for is off.
Related
I have three models: Course, Category, and Categorisation.
# course.rb
class Course < ApplicationRecord
has_many :categorisations, foreign_key: "course_id", dependent: :destroy
has_many :categories, through: :categorisations
end
#category.rb
class Category < ApplicationRecord
has_many :categorisations, foreign_key: "category_id", dependent: :destroy
has_many :courses, through: :categorisations
end
# categorisation.rb
class Categorisation < ApplicationRecord
belongs_to :course
belongs_to :category
validates :course_id, presence: true
validates :category_id, presence: true
end
Course controller has the following params:
def course_params
params.require(:course).permit(:name, :prerequisite, :description,
:user_id, :category_ids => [])
end
I'm trying to build the association via the Course form:
<%= form_for #course do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :prerequisite %>
<%= f.text_field :prerequisite, class: 'form-control' %>
<%= f.label :description %>
<%= f.text_area :description, class: 'form-control' %>
<%= f.label :category_ids, "Category" %>
<%= f.collection_check_boxes :category_ids, Category.all, :id, :name,
{}, { multiple: true, class: 'form-control'} %>
<%= f.submit "Create a new course", class: "btn btn-primary" %>
<% end %>
However when I click submit I get the validation error:
Categorisations is invalid
I'm unsure why this is the case?
Removing the presence validation on :course_id will fix the issue
class Categorisation < ApplicationRecord
belongs_to :course
belongs_to :category
# validates :course_id, presence: true
validates :category_id, presence: true
end
Problem:
Course is not saved but due to through association when you associate category with course rails first tries to save categorisation but course_id is not available yet
And this is the reason presence validation fails for course_id
Solution
Removing the validation is just a workaround the actual solution will be something interesting
NOTE: Even I am facing the similar issue a few days back. we have removed validation for now. I will be interested in any better solution
You're not able to save any entity that relates to Categorization in database because of presence: true action. Firstly, I think it's a better idea to validate :category_id in the schema.rb by category_id null: false. And secondly, are you sure you need the Categorization model? My suggestion is to think of putting a categorizationable.rb file in your yourApp/app/models/concerns folder and after that using its functionality in the existing models. You can read more about concerns here. This technique is really powerful when your models need refactoring.
Here is my models:
class Item < ActiveRecord::Base
has_many :price_group_lines
has_many :price_groups, through: :price_group_lines
attr_accessible :item_name, :item_id
validates :item_name, presence: true
def to_label
"#{item_name}"
end
end
class PriceGroup < ActiveRecord::Base
has_many :customers
has_many :price_group_lines
has_many :items, through: :price_group_lines
attr_accessible :price_group_name
validates :price_group_name, presence: true
def to_label
"#{price_group_name}"
end
end
class PriceGroupLine < ActiveRecord::Base
belongs_to :item
belongs_to :price_group
attr_accessible :item_id, :price_group_id, :price, :item, :price_group
validates :price, :item_id, :price_group_id, presence: true
end
View (show.html.erb) for Price Group controller
<h1> PRICE GROUP </h1>
<p> <%= #pg.price_group_name %> </p> <br>
<% #pg.items.each do |i|%>
<%= i.item_name %>
<% end %>
<br>
<%= link_to "PRICE GROUP List", price_groups_path %>
So, I have the access to item_name in show.html.erb, but i don't know how to get the access to "price" attribute in PriceGroupLine model. Something, like
i.items.price not working. Please, help!
Problem is because you have association of price, better said prices.
Your variable i is already item just call price_group_lines on it and you will get association on which you can call each and get each price.
<% #pg.items.each do |i|%>
<%= i.item_name %> # will display item name
<% i.price_group_lines.each do |pgl| %>
<%= pgl.price %> # will display price
<% end %>
<% end %>
I am having trouble trying to update nested models in my form. I don't get any errors and but the attributes don't get updated.
I have the following model:
class Trip < ActiveRecord::Base
has_many :segments
accepts_nested_attributes_for :segments, allow_destroy: true
end
class Segment < ActiveRecord::Base
belongs_to :start_location, class_name: 'Location'
belongs_to :end_location, class_name: 'Location'
belongs_to :trip
validates_presence_of :date, :start_location, :end_location
end
class Location < ActiveRecord::Base
has_many :segments
end
And have this code in the _form.html.erb:
<%= form_for #trip do |f| %>
...
<%= f.fields_for :segments do |builder| %>
<%= render 'segment_fields', f: builder %>
<% end %>
...
<% end %>
And this in the partial _segment_fields.html.erb:
<%= f.collection_select :start_location_id, Location.order(:name), :id, :name %> -
<%= f.collection_select :end_location_id, Location.order(:name), :id, :name %> <br>
<%= f.date_field :date %>
In my controller I also permited the assigment of :segments_attributes
def trip_params
params.require(:trip).permit(:name, :start_date, :end_date, :segments_attributes)
end
Does anybody know what I am lacking or doing wrong?
When you are creating a new record you don't need its id to be permitted as it's not been created but when you want to update your record you need to pass id to the permitted attributes, else it will work with create but not when you want to update your record, so you need to do:
def trip_params
params.require(:trip).permit(:id, :name, :start_date, :end_date, segments_attributes: [:id,:start_location_id,:end_location_id, :date])
end
I have a problem that's really confusing me.
I have four models, a Workout model, an Exercise model, a workout_exercise join model, and a workout_exercise_set model.
I can add exercises to workouts through the workout_exercises join table. Now I'm trying to add sets to exercises in workouts, which is what the workout_exercises_sets table is for.
Here's an example.
Workout
Exercise 1
Set 1
Set 2
Set 3
Exercise 2
Set 1
Set 2
Set 3
Exercise 3
etc.
In workouts/edit.html.erb I have a form where I can see all the exercises in the workout, and edit the number of sets by using form_for and fields_for, but when I try to update an exercise or the entire workout I get a MassAssignmentSecurity::Error in WorkoutsController#update that says Can't mass-assign protected attributes: workout_exercise_sets. I can't figure out how to get past this. I know I'm editing a workout, but I'm not trying to write to a field called workout_exercise_sets, so I'm really confused here. I really appreciate any guidance. All relevant code is below.
workout/edit.html.erb:
<%= form_for(#workout) do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %><br />
<%= f.label :description %><br>
<%= f.text_area :description %></br>
<%= f.fields_for :workout_exercises do |s| %>
<%= s.object.exercise.name %></b>
<%= s.fields_for :workout_exercise_sets do |set| %>
<%= set.label :set_number %>:
<%= set.number_field :set_number %>
<%= set.label :reps %>:
<%= set.number_field :repetitions %>
<%= set.label :rest_time %>(seconds):
<%= set.number_field :rest_time %>
<%= set.submit %>
<% end %>
<%= s.hidden_field :_destroy %>
<%= link_to "Remove exercise?", '#', class: "remove_fields" %>
<% end %>
<%= f.submit %>
<% end %>
Here is the workout model:
class Workout < ActiveRecord::Base
attr_accessible :name, :exercises_attributes, :workout_exercises_attributes, :exercise_order, :description
has_many :workout_exercises, dependent: :destroy, :order => "exercise_order DESC"
has_many :exercises, through: :workout_exercises
accepts_nested_attributes_for :exercises
accepts_nested_attributes_for :workout_exercises, allow_destroy: :true
end
Here is the exercise model:
class Exercise < ActiveRecord::Base
attr_accessible :name, :description
has_many :workout_exercises
has_many :workouts, through: :workout_exercises
validates :name, uniqueness: :true, presence: :true
validates :description, uniqueness: :true, presence: :true
end
Here is the workout_exercise model:
class WorkoutExercise < ActiveRecord::Base
attr_accessible :exercise_id, :workout_id
belongs_to :exercise
belongs_to :workout
has_many :workout_exercise_sets, dependent: :destroy
accepts_nested_attributes_for :workout_exercise_sets, allow_destroy: :true
end
and finally, here is the workout_exercise_sets model:
class WorkoutExerciseSet < ActiveRecord::Base
attr_accessible :repetitions, :rest_time, :set_number, :workout_exercise_id
belongs_to :workout_exercise
end
And for good measure, here is a diagram of the DB:
I think in your workout_excercise.rb file, you should add :workout_exercise_sets_attributes to your attr_accessible list.
class WorkoutExercise < ActiveRecord::Base
attr_accessible :exercise_id, :workout_id, :workout_exercise_sets_attributes
belongs_to :exercise
belongs_to :workout
has_many :workout_exercise_sets, dependent: :destroy
accepts_nested_attributes_for :workout_exercise_sets, allow_destroy: :true
end
I'm trying to get my User to save to a Rate. I was able to get the Location to be saved to the Rate by removing the presence validation but after it's created it doesn't have the current user. How would I do this for my nested form?
User.rb
attr_accessible :email, :password
has_many :locations
has_many :rates
Location.rb
attr_accessible :name, :rates_attributes
belongs_to :user
has_many :rates
accepts_nested_attributes_for :rates, :reject_if => :all_blank
# Not sure if :all_blank works anyways as it -
# still saves even when theirs no user_id, lol
Rate.rb
attr_accessible :amount, :location_id
belongs_to :location
belongs_to :user
validates_presence_of :amount
# Couldn't use these validations
# validates_presence_of :user_id
# validates_presence_of :location_id
LocationsController
def new
#location = Location.new
#location.rates.build
end
def create
#location = current_user.locations.build(params[:location])
if #location.save.....
end
locations/new.html.erb
<%= nested_form_for #location do |f| %>
<%= f.label :name, "Name *" %>
<%= f.text_field :name %>
<%= f.link_to_add "Add Rate", :rates %>
<%= f.fields_for :rates do |r| %>
<%= r.text_field :amount %>
<%= r.link_to_remove "Remove" %>
<% end %>
<%= f.submit "Add Location" %>
<% end %>
There is a great railscast on this topic; episodes 196 and 197. Even better, Ryan wrote a gem https://github.com/ryanb/nested_form.
The gem is super easy to implement. If set up correctly the nested form grabs the parent object id on create automatically.
I don't notice anything in the code you have posted that looks wrong...what does your nested form look like in the view?