Check params validity before save - ruby-on-rails

I need to verify if the model could be saved with a specific params but without saving it
event_params = {:user => #current_user.id, :id => id, :value => value}
If I wanted to save it I could verify it easily with something like
x = e.update_attributes(event_params) if x.true? .... end
I have read that I could use assign_attributes that would basically do the same as update_attributes but without saving.
The problem is that with the assign_attributes it always returns nil, so I think I have no way to verify it the params would be valid or not on the model.

You can call the valid? method on any instance of a model object, to have it go through validations and verify if this can be persisted. Note that this doesn't guarantee persistence as you might have other constraints at the database level.
Reference: http://api.rubyonrails.org/classes/ActiveRecord/Validations.html#method-i-valid-3F

Related

How to validate the presence of attributes when updating a record?

I am new to rails and I try to find a validation method corresponding to validate the presence of an attribute when updating a record. If that attribute is not present, meaning the attribute does not exist from the request body, Rails should not update the record.
validates :description, presence: true
and
validates_presence_of :description
doesn't seem to do the job. Is there a method for this purpose? It seems quite common in every day scenarios.
If you say:
model.update(hash_that_has_no_description_key)
then you're not touching :description: sending a hash without a :description key to update is not the same as sending in a hash with :description => nil. If model is already valid (i.e. it has a description) then that update won't invalidate it because it won't touch :description.
You say this:
If that attribute is not present, meaning the attribute does not exist from the request body, Rails should not update the record.
Since you're talking about the request body (which models really shouldn't know anything about) then you should be dealing with this logic in the controller as it prepares the incoming data for the update call.
You could check in the controller and complain:
data = whatever_params
if(!data.has_key?(:description))
# Complain in an appropriate manner...
end
# Continue as now...
or you could include :description => nil if there is no :description:
def whatever_params
data = params.require(...).permit(...)
data[:description] = data[:description].presence # Or however you prefer to do this...
data
end
maybe you should use before_update..
see this: http://edgeguides.rubyonrails.org/active_record_callbacks.html#conditional-callbacks
but use before_update instead before_save..

Validations for api build with rails

I have a model Person.
One controller Api::V1::PersonsController
In my controller:
def index
#persons = Person.new(user_id: #current_user.id, type_id: params[:type_id]).method
render json: #persons, status: :ok
end
In my model:
attr_accessor :user_id, :type_id
validates_presence_of :type_id
Also tried:
validates :type_id, :presence => true
When I create my Person with no type_id, I don't get any error, what else do I need to do, or is there a better way of doing this?
From the Rails guide validation section:
The following methods trigger validations, and will save the object to
the database only if the object is valid:
create
create!
save
save!
update
update!
The bang versions (e.g. save!) raise an exception if the record is
invalid. The non-bang versions don't, save and update return false,
create just returns the object.
When you create an object using the new method, the validation rules do not fire as the object is not persisted to the database.
You can call Person.save or Person.save! after Person.new or create a Person object using create or create!. Both of these methods persist the object to the database so a validation error will be raised.
Also, in your case, you can do something like this:
Person.new(user_id: #current_user.id, type_id: params[:type_id]).valid? # => false
This way, you can check if the object is a valid object and then proceed with the rest of your code.
.new is not going to persist your Person to the database.
Validation will not be carried out unless using .save after .new or in a .create or .create! method.
Check out point 1.2 here in Rails validation guides ›

update_attribute/s() is calling callback for save password

I'm trying to update single attribute of a user model from a admin controller (not users controller).
While doing this I tried update_attribute() but it was changing the users password also.
I think the password is changing because I have before_save method on user model which hashes the password.
update_attributes() is not working because it is checking the validations for password which is presence=>true
Is there any way to achieve this?
You can set a condition on your validations by using the :if option. In my code, it looks something like this:
validates :password,
:length => { :minimum => 8 },
:confirmation => true,
:presence => true,
:if => :password_required?
def password_required?
crypted_password.blank? || password.present?
end
So basically, it's only if the crypted_password in the database is not set (meaning a new record is being created) or if a new password is being provided that the validations are run.
Try update_column(name, value), it might work.
You can update single attribute of user like this
#user is that user whose attribute you want to update
e.g user_name
#user.update_attributes(:user_name => "federe")
Try it and it will only update one attribute..
ActiveRecord has an 'update-column' method that skips both validations and callbacks:
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column
However, I'd suggest that could be dangerous - you have that :before_save filter for a reason. If you place an :except method on the filter to circumvent in specific cases, it not only becomes reusable but you keep behaviour consistent and avoid having a method buried in a controller that's bypassing your Model's validation/callback stack.
I'm personally not overly keen on seeing methods like update_column anywhere except as protected methods inside Models.
Try :
To bypass callback and validations use :
User.update_all({:field_name => value},{:id => 1})
Just wanted to let you know :
In Rails, update_attribute method bypasses model validations, while update_attributes and update_attributes! will fail (return false or raise an exception, respectively) if a record you are trying to save is not valid.
The difference between two is update_attribute use save(false) where as update_attributes uses save or you can say save(true) .

Why doesn't my custom validation run in Rails?

This is my model:
class Goal < ActiveRecord::Base
belongs_to :user
validate :progress_is_less_than_max
private
def progress_is_less_than_max
if progress > max
errors.add(:progress, "should be less than max")
end
end
end
If I go into the console and do
some_user.goals.create! :name => 'test', :max => 10, :progress => 15, :unit => 'stuff'
it saves just fine, without any errors. What am I not doing right?
Well, that's not how you write a custom validator: your custom validator should inherit from ActiveModel::EachValidator.
See the bottom of this rails cast for an example of a customer validator: http://railscasts.com/episodes/211-validations-in-rails-3?view=asciicast
#jaydel is correct in that .create will return an instance of the model (regardless of if it is saved in the database or not).
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
However, calling .save! on the .create'd model or calling .create! to begin with will raise an exception if validations fail.
Creates an object just like ActiveRecord::Base.create but calls save! instead of save so an exception is raised if the record is invalid.
.save will run validations but returns false if they fail.
By default, save always run validations. If any of them fail the action is cancelled and save returns false. However, if you supply :validate => false, validations are bypassed altogether. See ActiveRecord::Validations for more information.

Rails: #update_attribute vs #update_attributes

obj.update_attribute(:only_one_field, 'Some Value')
obj.update_attributes(field1: 'value', field2: 'value2', field3: 'value3')
Both of these will update an object without having to explicitly tell ActiveRecord to update.
Rails API says:
update_attribute
Updates a single attribute and saves the record without going through the normal validation procedure. This is especially useful for boolean flags on existing records. The regular update_attribute method in Base is replaced with this when the validations module is mixed in, which it is by default.
update_attributes
Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will fail and false will be returned.
So if I don't want to have the object validated I should use #update_attribute. What if I have this update on a #before_save, will it stackoverflow?
My question is does #update_attribute also bypass the before save or just the validation.
Also, what is the correct syntax to pass a hash to #update_attributes ... check out my example at the top.
Please refer to update_attribute. On clicking show source you will get following code
# File vendor/rails/activerecord/lib/active_record/base.rb, line 2614
2614: def update_attribute(name, value)
2615: send(name.to_s + '=', value)
2616: save(false)
2617: end
and now refer update_attributes and look at its code you get
# File vendor/rails/activerecord/lib/active_record/base.rb, line 2621
2621: def update_attributes(attributes)
2622: self.attributes = attributes
2623: save
2624: end
the difference between two is update_attribute uses save(false) whereas update_attributes uses save or you can say save(true).
Sorry for the long description but what I want to say is important. save(perform_validation = true), if perform_validation is false it bypasses (skips will be the proper word) all the validations associated with save.
For second question
Also, what is the correct syntax to pass a hash to update_attributes... check out my example at the top.
Your example is correct.
Object.update_attributes(:field1 => "value", :field2 => "value2", :field3 => "value3")
or
Object.update_attributes :field1 => "value", :field2 => "value2", :field3 => "value3"
or if you get all fields data & name in a hash say params[:user] here use just
Object.update_attributes(params[:user])
Tip: update_attribute is being deprecated in Rails 4 via Commit a7f4b0a1. It removes update_attribute in favor of update_column.
update_attribute
This method update single attribute of object without invoking model based validation.
obj = Model.find_by_id(params[:id])
obj.update_attribute :language, “java”
update_attributes
This method update multiple attribute of single object and also pass model based validation.
attributes = {:name => “BalaChandar”, :age => 23}
obj = Model.find_by_id(params[:id])
obj.update_attributes(attributes)
Hope this answer will clear out when to use what method of active record.
Also worth noting is that with update_attribute, the desired attribute to be updated doesn't need to be white listed with attr_accessible to update it as opposed to the mass assignment method update_attributes which will only update attr_accessible specified attributes.
update_attribute simply updates only one attribute of a model, but we can pass multiple attributes in update_attributes method.
Example:
user = User.last
#update_attribute
user.update_attribute(:status, "active")
It pass the validation
#update_attributes
user.update_attributes(first_name: 'update name', status: "active")
it doesn't update if validation fails.
You might be interested in visiting this blog post concerning all the possible ways to assign an attribute or update record (updated to Rails 4) update_attribute, update, update_column, update_columns etc. http://www.davidverhasselt.com/set-attributes-in-activerecord/. For example it differs in aspects such as running validations, touching object's updated_at or triggering callbacks.
As an answer to the OP's question update_attribute does not by pass callbacks.
Great answers.
notice that as for ruby 1.9 and above you could (and i think should) use the new hash syntax for update_attributes:
Model.update_attributes(column1: "data", column2: "data")
update_attribute and update_attributes are similar, but
with one big difference: update_attribute does not run validations.
Also:
update_attribute is used to update record with single attribute.
Model.update_attribute(:column_name, column_value1)
update_attributes is used to update record with multiple attributes.
Model.update_attributes(:column_name1 => column_value1, :column_name2 => column_value2, ...)
These two methods are really easy to confuse given their similar names and works. Therefore, update_attribute is being removed in favor of update_column.
Now, in Rails4 you can use Model.update_column(:column_name, column_value) at the place of Model.update_attribute(:column_name, column_value)
Click here to get more info about update_column.
To answer your question, update_attribute skips pre save "validations" but it still runs any other callbacks like after_save etc. So if you really want to "just update the column and skip any AR cruft" then you need to use (apparently)
Model.update_all(...) see https://stackoverflow.com/a/7243777/32453
Recently I ran into update_attribute vs. update_attributes and validation issue, so similar names, so different behavior, so confusing.
In order to pass hash to update_attribute and bypass validation you can do:
object = Object.new
object.attributes = {
field1: 'value',
field2: 'value2',
field3: 'value3'
}
object.save!(validate: false)
I think your question is if having an update_attribute in a before_save will lead to and endless loop (of update_attribute calls in before_save callbacks, originally triggered by an update_attribute call)
I'm pretty sure it does bypass the before_save callback since it doesn't actually save the record. You can also save a record without triggering validations by using
Model.save false

Resources