What happens when params has more parameters than table columns - ruby-on-rails

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

Related

Set a field value outside a form in Rails

So I have a Rails app with a Post model. One of the fields is collection_id.
Each post should have the ID of the latest collection at the time of posting. If I have that ID in the backend, and I remove the form field for collection_id, how can I make sure this ID goes into the database without having a hidden_field in the Rails frontend?
The problem with a hidden field is that users could use a web inspector to change the value. This needs to be secure.
What's the best way to do this?
If you have that ID in your backend, you can pass it in your controllers action before saving:
#post = Post.create(params[:post])
#post.controller_id = variable_holding_the_id
if #post.save ...
or in some cases you can do it in the model with a callback:
after_create :set_collection_id
def set_collection_id
self.collection_id = variable_holding_the_id
end

when to use attributes hash

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.

has_many through blowing away the association's metadata on mass association

Hey,
Not a Rails noob but this has stumped me.
With has many through associations in Rails. When I mass assign wines to a winebar through a winelist association (or through) table with something like this.
class WineBarController
def update
#winebar = WineBar.find(params[:id])
#winebar.wines = Wine.find(params[:wine_bar][:wine_ids].split(",")) // Mass assign wines.
render (#winebar.update_attributes(params[:wine_bar]) ? :update_success : :update_failure)
end
end
This will delete every winelist row associated with that winebar. Then it finds all of the wines in wine_ids, which we presume is a comma separated string of wine ids. Then it inserts back into the winelist a new association. This would be expensive, but fine if the destroyed association rows didn't have metadata such as the individual wine bar's price per glass and bottle.
Is there a way to have it not blow everything away, just do an enumerable comparison of the arrays and insert delete whatever changes. I feel like that's something rails does and I'm just missing something obvious.
Thanks.
Your problem looks like it's with your first statement in the update method - you're creating a new wine bar record, instead of loading an existing record and updating it. That's why when you examine the record, there's nothing showing of the relationship. Rails is smart enough not to drop/create every record on the list, so don't worry about that.
If you're using the standard rails setup for your forms:
<% form_for #wine_bar do |f| %>
Then you can call your update like this:
class WineBarController
def update
#winebar = WineBar.find(params[:id])
render (#winebar.update_attributes(params[:wine_bar]) ? :update_success : :update_failure)
end
end
You don't need to explicitly update your record with params[:wine_bar][:wine_ids], because when you updated it with params[:wine_bar], the wine_ids were included as part of that. I hope this helps!
UPDATE: You mentioned that this doesn't work because of how the forms are setup, but you can fix it easily. In your form, you'll want to rename the input field from wine_bar[wine_ids] to wine_bar[wine_ids_string]. Then you just need to create the accessors in your model, like so:
class WineBar < ActiveRecord::Base
def wine_ids_string
wines.map(&:id).join(',')
end
def wine_ids_string= id_string
self.wine_ids = id_string.split(/,/)
end
end
The first method above is the "getter" - it takes the list of associated wine ids and converts them to a string that the form can use. The next method is the "setter", and it accepts a comma-delimited string of ids, and breaks it up into the array that wine_ids= accepts.
You might also be interested in my article Dynamic Form Elements in Rails, which outlines how rails form inputs aren't limited to the attributes in the database record. Any pair of accessor methods can be used.

How to Inspect Validations that exist on ActiveRecord::Base Objects

I want to create a helper method to automatically output text_fields with a :maxlength attribute. I want this maxlegth to be set based on the :max specified in the field attributes validates_length validation, so as to remain DRY.
My question is: Is there a good way to inspect the validations that are present on an objects attribute, and then extract the maximum length from that validation.
I also plan on extracting other things like the regex from validates_format so I can set an attribute on the text_field, which can then be used by js to run client side validations.
Thanks.
Bonus points: Why doesn't Rails Automatically add the maxlength to text fields for us?
In Rails 3 you can call the _validators method on an object to get the list of validators that will run:
t = Ticket.new
t._validators
Well, I don't know if this is a 'good' way, but you can initialize a new object, call #valid? on it and then call #errors to get a hash of attributes and error messages. Then you'd have to parse those errors messages.
user = User.new
user.valid?
errors_hash = user.errors

Creating model objects from raw form data - is a one-to-one field match required?

If I say this in the controller:
#order = Order.new(params[:order])
What is required for this to work?
Does there need to be a one-to-one match between all of the fields in params[:order] and the Order model?
Or can there be more or fewer fields in params[:order] than are required to instantiate an Order?
params[:order] itself should be a hash, where each key is the name of the model field. To see how Rails converts form field names into the params hash, write a view template with the form_for helper and view source.
There can be more or fewer fields, yes. Extra fields will be ignored. Fewer fields just won't be copied into the instance object. You don't need anything at all to instantiate an ActiveRecord object. (Object validity and saving are a different story - they invoke validations and the ActiveRecord callback mechanism.)
There can indeed be fewer fields.
Make sure you have all fields necessary for any validations though!

Resources