Weird "Can't mass-assign protected attributes" error - ruby-on-rails

Im running into the following error
Can't mass-assign protected attributes:
I know this is a generally asked question, but none of the answers provided seem to work for me. The thing is, i already have this 2 lines in my "parent" model:
accepts_nested_attributes_for
AND
attr_accessible :childmodel_attributes
I had run into this error in the past, and those 2 lines succefully solved the issue.. But they dont now. BTW, the "protected attributes" are ALL the fields in my "child" model, not just certain ones.
Hope someone can help me, im stuck and dont know what else to do.
This is the full error line:
Can't mass-assign protected attributes: linea, origen_comp, conector, char_ini, char_fin, modopremio_id, codigo_opc, ochar_ini, ochar_fin
This is what the "child_model" has
:consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :onchar_fin
(Sidenote, the "parent/child model" names are just for reference, they are not their true names (productosacierto AND productosregla) would be it.
Parent Model (Productosacierto)
has_many :productosregla
accepts_nested_attributes_for :productosregla, :reject_if => :all_blank, :allow_destroy => true
attr_accessible :productosregla_attributes, :producto_id, :consorcio_id, :clave, :descripcion, :una_condicion
default_scope order: 'id'
self.table_name = "hproductos_aciertos"
Child Model (Productosregla)
self.table_name = "hproductos_reglas"
belongs_to :productosacierto
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :ochar_fin
default_scope order: 'id'

If this is an exact copy of your child model, then I guess the comma at the end of the attr_accessible line is the problem!

I feel kind of embarrassed right now. There were several sintax errors on my model and thats what was causing this issue. I took it for granted that rails would throw a sintax error if there were some, specially when it's so delicate when it comes to writting code in the model...
This is what i had:
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :ochar_fin
This is what it was supposed to be:
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_ini, :char_fin, :modopremio_id, :codigo_opc, :ochar_ini, :ochar_fin
As you can see, there was a ":" missing before the codigo_opc param, and also the char_ini param had an extra "c" that didnt belong. Thanks everyone who helped, this taught me to make sure never to assume anything.

Related

validation error and possibly strong parameters with "Can't mass-assign protected attributes"

I am migrating an old app to strong parameters and a bit of a wierd issue has come up. I have a Location model and the abbreviated version is:
class Location < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
validates :name, presence: true
end
The problems is that this name variable is giving me a mass-assignment error even when called from the rails cli. Such as:
But I'm not really sure why. Another model Comment is just fine (no validation issue or mass-assignment warning) with or without validations making me thing it isn't some installation issue but possibly somehow either a collision of the name variable or some other issue. Any ideads on how to fix this?

Why won't rails create associated records/objects from nested form using strong parameters?

I'm trying to create a record and it's associated records from a nested form using strong parameters. My primary model is:
class MaterialDonationRequest < ActiveRecord::Base
has_many :donation_items, dependent: :destroy
accepts_nested_attributes_for :donation_items, allow_destroy: true
validates :name, presence: true
attr_accessor :due_on_event, :date, :donation_items_attributes, :event_id
end
My associated (nested) model is:
class DonationItem < ActiveRecord::Base
validates :name, presence: true
belongs_to :material_donation_request
belongs_to :global_profile
validates :name, presence: true
attr_accessor :_destroy
end
In my material_donation_requests_controller.rb, I have the following for strong parameters:
def material_donation_request_params
params.require(:material_donation_request).permit(:name, :description, :event_flag, :due_on_event, :date, :event_id, donation_items_attributes: [:id, :name, :description, :amount, :_destroy])
end
Here's the line in my create method where I create the object:
#material_donation_request = MaterialDonationRequest.new(material_donation_request_params)
After doing this, #material_donation_request is created and populated correctly from the form. But the associated donation_items do not get created. For instance, in the debugger, when I enter #material_donation_request.donation_items.first, Rails returns nil.
For reference, here is what Rails returns for material_donation_request_params in the manual tests I'm running:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"", "amount"=>"2", "_destroy"=>""}}}
Why isn't Rails creating the associated objects from the form as well? Everywhere I've looked, it seems like this structure should create everything, and a subsequent save should save everything (or at least throw validation errors as in this case-see update below). Is there something I'm missing?
Update
Since it was brought up in the answers, yes, the material_donation_params shown above would not pass validation. That's the scenario I've been manually testing. It should generate a validation error on save, but instead, simply saves the MaterialDonationRequest with no errors of any kind, and saves nothing to DonationItems.
To be clear, though, if I fill out the form completely and get the following material_donation_request_params:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"first", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"second", "amount"=>"2", "_destroy"=>""}}}
and then do #material_donation_request.save, it only saves the MaterialDonationRequest, and not any of the DonationItems.
Final Update
Okay. I've deleted my previous "final update" because what I wrote, and what I wrote in some of the comments was wrong. What ended up fixing this was not an update to Rails 4.1.8. I ran the bundle update command before actually saving the gem file with the new Rails version. So really, what ended up fixing this was simply updating all the gems that didn't have fixed version numbers. God only knows why things weren't working with the previous set of gems. Sorry that this isn't so helpful...
From Rails Validations guide
presence
This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either nil or a blank string, that is, a string that is either empty or consists of whitespace.
You are requiring donation_item to be present, but your resulting params hash clearly has donation names blank, validation is failing. Calling save! when debugging these things can be helpful since it would throw a error on failure.
I figured out the answer. In total desperation, I upgraded my Rails version from 4.0.2 which is what I had been using, to 4.1.8. After doing this, with no other changes whatsoever (except gem dependencies, of course), it just started working the way it's supposed to. So I guess Rails 4.0.2 has a problem with nested forms and strong parameters.

Issue with collection `build` method record instantiation in Rails 4.1.1

I'm having a problem with the Rails collection.build(attrs) method, specifically with how the framework instantiates a new record. For example, here is a much simplified view of my models:
class Document < ActiveRecord::Base
belongs_to :account
has_many :descriptions, before_add: :id_test
validates :account, presence: true
def id_test
puts self.account_id
end
end
When I do something like:
current_account.documents.build(:descriptions => [desc])
then id_test prints nothing. That is, the account_id is not set in the before_add callback (and yes, I've tried the after_add callback as well; account_id is not set in that case either).
If I do:
d = current_account.documents.build
d.assign_attributes(:descriptions => [desc])
Then everything works as expected. However, I would prefer a better alternative, since this would be a pain to implement in the controllers...
Is there a way to get Rails to add the foreign_key first, or is there some better way to set this up? I haven't gone back to check for sure, but this seems different than the way Rails 3 evaluated collection.build statements.
EDIT
Looking through the Rails code a bit, I see I can do:
current_account.documents.build do |record|
record.assign_attributes(:descriptions => [desc])
end
and everything works as expected. Although a bit more verbose, I guess this is technically more accurate anyway.
I would suggest using
Document.build(:account_id => current_account.id, :descriptions => [desc])

Parent model method is undefined

I'm an absolute beginner to Ruby on Rails; I literally started last night. I'm following the Getting Started with Rails tutorial, although I've made a few modifications. Instead of a blog with posts and comments, I'm making a simple task-tracking application in which Projects have Tasks associated with them.
Everything was going quite well until step #9 (Deleting Comments). According to the tutorial, I should be able to get the parent model for a comment by calling comment.post, the analogue of which in my case is task.project. But when Rails tries to render the partial in which I've got task.project, it raises a NoMethodError:
undefined method `project' for #<Task:0x7fb0011cf058>
My models are as follows:
class Project < ActiveRecord::Base
validates :name, :presence => true
has_many :tasks, :dependent => :destroy
end
class Task < ActiveRecord::Base
validates :name, :presence => :true
belongs_to :project
end
Using the rails console, I determined that my tasks do have a project_id method which returns the ID of the associated project. Thus, I can get around the problem by using Project.find(task.project_id) instead of task.project in my partial. That seems "wrong," though, and makes me wonder if the relationship between my two models is somehow broken.
What's going on here?
Update: If I comment out the validates line in the Task model, then all of its methods work properly. I don't understand why that is the case, though.
Update 2: Figured it out. I was using the symbol :true instead of the simple boolean value in the validates line of my Task model. Had I paid more attention to the stack trace in the first place, I would have figured it out much sooner. What an embarrassing mistake!
Your code looks perfect. But if you were trying that into console try reloading first and then try again
reload!
task = Task.find(<record-id>)
task.project
It should be working.

ruby on rails has_many validations

Lets say there is an error in shirt. will the error appear also in person.errors ?
and if so, how can i reach it ? ( i don't want to use person.shirt.errors)
class Person < ActiveRecord::Base
has_one : shirt
has_many : pants
validates :name, :presence => true
validates_length_of :name, :minimum => 3
end
person = Person.new(:name => "JD")
person.shirt.create(:color=> "red")
person.pants.create(:type=> "jeans")
person.valid?
According to this post it seems errors on child entities will be copied to the parent during a save, see here -> Validations section although this might have been changed since
Validations simply work as you'd expect;
#valid? will also validate nested models,
#save(false) will save without validations, etc.
The only thing to note is that all error messages
from the nested models are copied to the parent errors
object for error_messages_for. This will probably change
in the future, as discussed on the ticket, but that's
outside of the scope of this patch.
It should load into "person.errors". You can reach it by calling
person.errors.at(:<replace_this_with_name_of_attribute>)
You can also call
person.errors.each { |attr, msg| puts "attr = '#{attr}', msg = '#{msg}'" }
to check for all the error attribute names and the corresponding error messages. Good luck!

Resources