Can't mass-assign protected attributes - ruby-on-rails

Updating the code formatting for better viewing.
Folks,
I have been looking at this for sometime but I don't understand what could be messing up here. I am using Devise.
class User < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses
# Other stuff here
end
class Address < ActiveRecord::Base
belongs_to :user
validates_presence_of :zip #:street_address1,
end
-------------------- log output begin ------------------------------
Started POST "/users" for 127.0.0.1 at
2011-05-28 11:43:27 -0700 Processing
by RegistrationsController#create as
HTML Parameters: {"utf8"=>"√",
"authenticity_token"=>"CEmdqlsmdYa6Jq0iIf5KAxxISsUCREIrFNXWkP80nhk=",
"user"=>{"email"=>"a2#gmail.com",
"password"=>"[FILT ERED]",
"addresses_attributes"=>{"0"=>{"street_address1"=>"234
Pitkin Ct.", "zip"=>"12456"}}},
"commit"=>"Sign up"} WARNING: Can't
mass-assign protected attributes:
addresses_attributes SQL (0.0ms)
BEGIN SQL (164.0ms) SHOW TABLES
User Load (0.0ms) SELECT users.id
FROM users WHERE (users.email =
BINARY 'a2#gmail.com') LIMIT 1 SQL
(1.0ms) ROLLBACK
-------------------- log output end ------------------------------
The zip is present in the data posted and the posted data seems to be formatted properly. On the web page form I am getting the error that "Addresses zip can't be blank". I have dug around for what causes the "Can't mass-assign protected attributes" warning but haven't found anything that will help me.
Thanks for your thoughts and pointers.
-S

Have a look here and learn :)
http://railscasts.com/episodes/26-hackers-love-mass-assignment
Edit:
Having accepts_nested_attributes_forin User model enables you to send the data to the Address model.
Then, in the Address model, you have to set the requested attr_accessible

Inside of SpecificModel (appfolder/app/model/specific_model.rb)
Try using
attr_accessible :addresses_attributes, :another_attribute_to_make_mass_assignable, :another_attribute, etc.

Nowadays (April 2013) you should start to use https://github.com/rails/strong_parameters

Just include the datafield in the model as mentioned below
attr_accessible :addresses_attributes

Related

Rails accepts nested attributes and mongo error on delete

I'm using models on Mongo and Rails' accepts_nested_attributes_for.
For specific scenerio, when I try to add attachment_ids in first item and also I remove second item I receive mongo error:
[40]: Updating the path 'section9.significant_events.0.attachment_ids' would create a conflict at 'section9.significant_events' (on localhost:27017, legacy retry, attempt 1)
Here are the params passed to controller:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"xxxxx",
"section9"=>
{
"significant_events_attributes"=>
{"0"=>{"_destroy"=>"false", "description"=>"", "info_location"=>"", "attachment_ids"=>["", "62f4bd4fba962ee7ae1e96fd"], "id"=>"62f4b604ba962ee7ae1e96bd"},
"1"=>{"_destroy"=>"1", "description"=>"", "info_location"=>"", "attachment_ids"=>[""], "id"=>"62f4b772ba962ee7ae1e96ca"}}
},
"commit"=>"Save",
"section_map"=>"section9"}
And the relations are:
Section9
embeds_many :significant_events
accepts_nested_attributes_for :significant_events, allow_destroy: true
SignificantEvent
has_and_belongs_to_many :attachments, class_name: 'Edoc'=
embedded_in :section9
I think field should appear either in $set, or in $setOnInsert. But it should not be in both.

Rails virtual attribute in form not updating db from form or console

So in my Rails web application I have a section of code that prompts underage users to input a parent/guardian email when creating an account. In my database I would like to store this as the corresponding ID but naturally the user will want to enter an email.
this part of my form looks like this:
<%= f.label :guardian_email, "Guardian Email" %>
<%= f.text_field :guardian_email, class: 'form-control' %>
The corresponding code in my model looks like this:
def guardian_email
User.find_by_id(guardian_id).email if guardian_id
end
def guardian_email=(g_email)
guardian = User.find_by_email(g_email)
print guardian.id
self.guardian_id = guardian.id
print self.guardian_id
end
(the print lines I've added while debugging) However, submitting the form does not update the desired attribute. I then tried from the console and typed:
User.find_by_email("example-5#site.org").guardian_email=("example-7#site.org")
I get the output:
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example-5#site.org"], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example-7#site.org"], ["LIMIT", 1]]
88 => "example-7#mtb.org"
but when I load the user afterwards the attribute guardian_id is still nil. what is odd is that both of the print statements return the proper value (in this case 8) but the assignment has never occured, despite the fact the second 8 that is printed out is the value "self.guardian_id".
I've followed railscasts #16 - virtual attributes to arrive at the code I have now, Thanks.
EDIT:
The reason the above code would not function is because I was permitting the actual attribute in the controller as opposed to the virtual one. Although as max pointed out several other bad practices are also present in this code.
Something I still do not understand though is that calling the guardian_email=() method in the console still does not update the attribute, but it works fine on the site.
Thats a very convoluted approach to a simple task.
class User < ApplicationRecord
belongs_to :guardian,
class_name: 'User',
optional: true
def guardian_email=(email)
self.guardian ||= User.find_by_email(email)
#guardian_email = email
end
def guardian_email
#guardian_email || guardian.try(:email)
end
end
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :email
# ...
t.belongs_to :guardian, foreign_key: { to_table: :users }
t.timestamps
end
add_index :users, :email, unique: true
end
end
You'll also want a custom validation which kicks in if guardian_email is not blank which checks if the email actually belongs to a user.
class User < ApplicationRecord
# ...
validate :guardian_exists, unless: ->{ self.guardian_email.blank? }
# ...
private
def guardian_exists
errors.add(:guardian_email) unless guardian || User.find_by_email(guardian_email)
end
end
The reason no change was being made is because in the user controller I was including guardian_id as opposed to guardian_email as a permitted param. When attempting to update a model using virtual attributes, in the controller one should permit the virtual attribute(s) contained in your form.

Validating presence of nested attributes returns error "no method :path_base"

I have a model which accepts nested attributes. There are 4 attributes altogether and I need to verify the presence of one. the specific attribute I need to verify for is called path_base so I tried
validates_presence_of :path_base
In the model but I am getting the error
undefined method `path_base' for #<Template:0x007fa279146360>
When saving the template record. The params getting sent look like this
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZO+Pi3/6WwNk0H3cFhgDbRywjrAOv2RnZ7olIsenND0=", "already_saved"=>"false", "update_pages"=>"false",
"template"=>{"type"=>"singleton", "name"=>"test",
"template_responses_attributes"=>{"0"=>{"path_base"=>"", "liquid_code"=>"test", "indexable"=>"1", "content_type"=>"text/html"}, "1"=>{"path_base"=>"", "liquid_code"=>"", "indexable"=>"1", "content_type"=>"text/html"}},
"template_fields_json"=>"[\r\n\r\n]"}, "button"=>""}
So inside the template_responses_attributes array is where the value of path_base is, and that is inside the template array just like normal (template is the controller/model that is saving the record that accepts the nested attributes).
If anyone could point me in the correct direction for this it would be greatly appreciated.
I did try this, which I found here but it did not return an error when the value was empty.
reject_if: proc { |attributes| attributes['path_base'].blank? }
Each model should only be responsible for validating its own attributes - if you want to ensure that the nested records are valid use validates_associated.
class Template < ApplicationRecord
has_many :responses
accepts_nested_attributes_for :responses
# This validates all the associated records
validates_associated :responses
end
class Response < ApplicationRecord
validates_presence_of :path_base
# ...
end
The reject_if option is not a validation mechanism. Rather it lets you filter out nested attributes if they do not meet a criteria, take for example a task list application where you would want to filter out the empty rows.

Ruby on rails .try() not working correctly

I am trying to do the following:
<%= order.user.try(:first_name) %>
<%= order.user.try(:last_name) %>
<%= order.user.try(:email) %>
This works however when I view the index of this page, the data does not appear in the table. I also viewed the logs and saw that the relevant data is being posted
Started POST "/orders" for 127.0.0.1 at 2013-02-18 11:15:02 +0000
Processing by OrdersController#create as HTML Parameters:
{"utf8"=>"?",
"authenticity_token"=>"Rjf7+Z8UAPh9ZofIrOs+p6AoGPfgd/6rfdTRqJLtx0g=",
"user"=>{"first_name"=>"Test", "last_name"=>"TestHome",
"email"=>"test#test.com"}, "order"=>{"address"=>"3433234243",
"card_number"=>"45423554213994", "card_type"=>"visa",
"card_expires_on(1i)"=>"2013", "card_expires_on(2i)"=>"2",
"card_expires_on(3i)"=>"1"}, "commit"=>"Confirm Order"} Cart Load
(0.0ms) SELECT carts.* FROM carts WHERE carts.id = 8 LIMIT 1
so I alternatively tried to do
<%= order.user.first_name %>
<%= order.user.last_name %>
<%= order.user.email %>
Doing this resulted in
NoMethodError in Orders#index
Showing C:/Users/sites/e-smart/app/views/orders/index.html.erb where line #18 raised:
undefined method `first_name' for nil:NilClass
I understand that the result of this error is because first_name is empty.
What is the best way to get the data.
Order.rb
class Order < ActiveRecord::Base
attr_accessible :address, :card_expires_on, :card_type, :ip_address, :card_number, :first_mame, :last_name, :email, :quantity
belongs_to :cart
has_many :line_items, :dependent => :destroy
belongs_to :user
User.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
has_many :role_users
has_many :orders
You are saying:
I understand that the result of this error is because first_name is empty.
This is not correct. The cause of the error is that user is nil. This is also in line with the behavior or try() to return -blank-.
Make sure you have an order with the user populated correctly. This means your order table must have a user_id and the user must be assigned correctly.
You're comparing two different things here - the try is working correctly in your view, it's returning nil when you call it on nil (undefined method 'first_name' for nil:NilClass is because order.user is nil, not because first_name is nil.
This is unrelated to your POST - what is the actual issue you're trying to solve here?
Let me clarify the meaning of this error first.
NoMethodError in Orders#index
Showing C:/Users/sites/e-smart/app/views/orders/index.html.erb where line #18 raised:
undefined method `first_name' for nil:NilClass
The above error doesn't mean that first_name for user is empty, but the user object itself is nil. It means that, your #order.user is nil and not #order.user.first_name is empty.
So, order.user.try(:first_name) should be read as print the first_name of the user who placed this order if there is an user associated with the order, otherwise do nothing but don't throw any error
The parameters in the form are being sent properly to your controller and may be you are missing something in the controller that associates the order and user records. Look into the controller action that is saving the object and make sure you are associating order and user properly.
Hope this helps

Can't mass-assign protected attributes even if I use attr_accessible

I'm working on a Rails 3.2.2 application which has JSON APIs and I use a
CLI client for inserting some data. It works fine except for the Author
model. When I try to create a new post (Post belongs_to :author and
Author has_many :posts) I get the following error :
<h1>
ActiveModel::MassAssignmentSecurity::Error in PostsController#create
</h1>
<pre>Can't mass-assign protected attributes: name</pre>
I did lots of researches on the topic but I found no working solution
:-(
I use attr_accessible to avoid MassAssignent errors and it works for all
others models but not for the "Author" name attribute.
Here is the Author model :
class Author < ActiveRecord::Base
attr_accessible :name, :email
extend FriendlyId
friendly_id :name, use: :slugged
# some validations
has_many :posts
#authlogic
acts_as_authentic
# some stuffs
end
Actually, I have disabled whitelist_attributes and it solved my problem
but I suppose that it is not the convenient way to do this (and probably
not a good idea).
My questions are : Why the attr_accessible does not work here ? And how
can I solve the problem without disabling the whitelist ?
Thank you,
Revan
EDIT :
The method which creates the new post :
def create
#post = Post.new(params[:post])
#post.author = current_author
# respond to etc.
end
current_author finds the author using a given API Key.
I found the solution ! :-)
The problem was that I used acts_as_taggable_on_steroids plugin which does not work on Rails 3.2 ...
Since "Author" is the only model which has a :name attribute, I thought that the problem came from Author ... but the problem was in the Tag model (which is in the acts_as_taggable_on_steroid plugin). Indeed, its :name attribute is not "accessible".
So, I use the acts_as_taggable_on gem (https://github.com/mbleigh/acts-as-taggable-on)
which correctly works on Rails 3.x

Resources