when to use attributes hash - ruby-on-rails

In this SO answer undefined method `stringify_keys!' ruby on rails, the OP tried to create a new car object with a 'Honda' string to the
#car = Car.new(params[:car])
and got a stringify keys error. The person who answered said that he had to specify the column from the table (in this case the 'name' column) when creating the object ,
create expects to get an attributes hash and to stringify it's keys for the column names.
If you have a column named name in your cars table then try this:
#car = Car.new(:name => params[:car])
However, I'm watching a RailsCast where Rbates creates an entry table with 'name' string and a column 'winner' as a boolean. In his controller (which he's set to respond with json), he does not use an attributes hash. His create action
def create
respond_with Entry.create(params[:entry])
end
Why didn't Rbates have to use an attributes hash, and, if he could have, what would that attributes hash look like? Something like this? Do you have to name every column, in ryans case :name and :winner?
def create
respond_with Entry.create(:name => params[:entry][:name], :winner => params[:entry] [:winner]
end

It all depends on what params[:car] contains. In the Railscast example, params[:car] is a Hash containing two entries (name and winner). In the other SO question it looks like params[:car] was a String containing the name of a car.
Car.new will always expect a Hash. If you want to pass a single value you need to turn it into a Hash with a key that tells Car.new what value you're passing.

In the previous question, params[:car] was a string. Rails's new and create methods both expect hashes, which is why the code was altered to pass in :name => params[:car]
If you use Rails's form_for to construct your forms, params[:model_name] will have a hash which keys matching model attributes. In that case, no custom work is necessary, and simply initializing the model with params[:model_name] works fine.

Related

Strong parameters for array of records

Strong parameters has me very confused. I'm writing a form to create several records at once. They are passed in params as an array of attributes:
{ :appointments => [ { :field1 => 'value1'
, :field2 => 'value2'
}
, # next record
]
}
Then in the controller I would like to do something like
params[:appointments].each do |a|
app = Appointment.create! a
end
But I run into lots of trouble with strong parameters, in the form of ForbiddenAttributeErrors. I've tried using appointment_params and whitelisting attributes, but with no luck. I can't find any good documentation matching my use case. They all assume the array of records should be nested below some owner record but this is not the case here.
Any help would be appreciated.
Make sure you are white listing your array in addition to the actual model attributes.
It seems like you have used the scaffolded version of the params.require method and not have updated that method when you changed your controller to deal with an array of appointments rather than one appointment at a time.
Something like this should work:
params.require(:appointment).permit(:field1, :field2, appointments: [:field1, field2])
or
params.require(:appointments).permit(:field1, :field2)
Not sure exactly what the rest of yoru code looks like, but it seems like you're not permitting the array itself, the above code samples attempt to white list what I would assume that attribute might be named.
If you are only using the attributes to create a new Appointment record, you can use the following
Appointment.create!(params.permit(applications: [:field1, :field2])[:applications])
If you really want to iterate over the array, you can do
params[:appointments].each do |a|
app = Appointment.create!(a.permit(:field1, :field2))
end

What happens when params has more parameters than table columns

In rails this type code automatically generated
#post = Post.new(params[:article_post])
#post.save
What happens when there are more parameters than database table columns? Say in database table we have column post_name post_id and in form I have a checkbox also along with another input field which do not need to save in database table but need for validation. In this case how the above code works. I want to know the basics.
Thanks
#post = Post.new(params[:article_post])
#post.save
Rails accepts only those parameters from request that are matched with table attributes.
For your check box validation, you can check manually like :
if params[:check_box_attributes_name]
#post = Post.new(params[:article_post])
#post.save
end
#post = Post.new(params[:article_post])
#post.save
This code save the columns which are matched with model attributes and assign them with given values and save in the database, And columns which are posted from form but didn't have matched attributes in database will not affect the code.
I assume you use Rails 3, since Rails 4 has different behavior. When you send new, create, attributes= or some other messages and pass a hash (and params[:article_post] is a hash), rails internally iterates through the hash and calls #{param_name}= method on model object. That is,
Post.new(:name => 'hello', :something_not_in_db => "Amazing!")
Is equivalent to
post = Post.new
post.name= 'hello'
post.something_not_in_db= "Amazing!"
Actually, rails first checks that all options that you pass in hash are allowed to be set with attr_accessible. But then it does not matter if your model table has column or not, it only matters that is responds to attribute_name= message

How to get serialized column list on RoR

I'm user of Ruby on Rails.
I made active model like this.
class User < ActiveRecord::Base
attr_protected :id
serialize :user_info, Hash
serialize :user_auth, Array
serialize :user_addr
end
I want to get the column names which contains a serialized object and type.
I'm looking forward to the method like this.
ex )
User.serialized_columns #=> {:user_info => Hash, :user_auth => Array, :user_addr => nil}
Would you help me?
Yes, you can get it using the method serialized_attributes which returns a Hash where key is the column name and value is the class of the column
So, you can get the serialized columns as
User.serialized_attributes
But you have modify the value of each key according to your requirement.

Setting Rails model attributes order

When defining a virtual setter method that relies on another method to be set, it appears that the order of the attributes being set in the hash matters. Is there a way around this while still mass-assigning attributes?
https://gist.github.com/3629539
EDIT
The condition in the real code, not shown in the example, is checking for the existence of an associated object. If the object exists, set a value. If not, ignore the value passed in. However, I am also using accepts_nested_attributes_for. So, the attribute hash may contain the attributes for the association. In which case, the object will exist.
{:name => 'Fred', :nested_attributes => {:color => 'red'}}
Name will not be set because the model will not exist.
{:nested_attributes => {:color => 'red'}, :name => 'Fred'}
accepts_nested_attributes_for will build a Nested instance, then set the attributes. When the name is to be set, the instance will exist and the nested attribute will be set.
Had a similar issue, and I came to the following reasonably generic solution:
def assign_attributes(new_attributes)
assign_first = new_attributes.extract!(:must_be_set_first, :must_also_be_set_first)
super(assign_first) unless assign_first.empty?
super(new_attributes)
end
Using super with the extracted param values you need set first ensures you handle all the weird special cases for attribute assignment (is it a reference? a params hash? a multi-value param?). Calling assign_attributes repeatedly with parts of a hash really should have the same effects as calling it with the whole hash once - this should be reasonably safe.
The only solution I can think of right now is to override the attributes setter...
def attributes=(attrs)
self[:dont_set_name] = attrs.delete(:dont_set_name)
super
end

Given a Model, how to loop through all properties?

I want to loop through all the properties of my 'user' model, how can I do this?
If you have an instance of your model then user.attributes is a Hash of the model's attributes and their values so, for example, you can do something like:
user.attributes.each_pair do |name, value|
puts "#{name} = #{value}"
end
If you don't have a specific instance then the class has methods that return information about the fields in the database e.g. User.columns and User.content_columns. e.g.
User.columns.each do |column|
puts column.name
end
Article.columns.each do |column|
puts column.name
end
This iterates over all the column objects for the Article model.
#model.methods returns the names of all methods of the object.
#model.methods.grep(/=$/) will return you the names of all write methods, so you can guess that if you have a setter, then you also have a reader, so this may be a "property".
You may also inspect the attributes hash (#model.attributes) which is a Hash with all the columns defined in the database, and this may be the most reliable way, since the "methods" method may not include the attribute readers (and writers) generated dynamically. (It may depend on the version of RubyOnRails you are using).

Resources