update_attribute/s() is calling callback for save password - ruby-on-rails

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) .

Related

Validating password with validates_format_of

EDIT: I realized the comments about there not being a difference with :save already covered were correct, and was able to work through some errors. However, it still appears the regex validation is failing for :password.
I'm a little confused, and think the problem might be related to there being only :password_digest in the table itself, while we use password and password_confirmation as attributes in the model. But I'm also guessing Rails has this all worked out and takes care of everything with the has_secure_password function.
validates_format_of :password, :with => /\A[a-zA-Z]\z/, :on => :create, :update fails with the string password.
Obviously, this doesn't make sense and matches fine in the console (using =~ or .match(). Also notice if I set allow_nil: true for :password other tests involving the user start failing (which doesn't make sense, as the password should never be nil anyways).
I don't see any use case where you need on create and update together if on: :save is already there.
For more model callbacks please refer this.
According to rails docs
The default behavior for all the built-in validation helpers is to be run on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use on: :create to run the validation only when a new record is created or on: :update to run the validation only when a record is updated.
So, you should not use :on option in your validation

Is there a better way of validating a non model field in rails

I have a form field in ROR 4 app called as 'measure'. It is not a database column, but its values will help model create child entries of its own via acts_as_tree : https://github.com/rails/acts_as_tree
I have to throw a validation when 'measure' is invalid. So I have created a virtual attribute known as measure and check for its validations only on a certain condition.
model someModel
attr_accessor :measure
validates_presence_of :measure, :if => condition?
Problem is when I am saving the code, I am thrown a validation which is fine. I am also thrown the same validation when I am trying to update the record in some other method of the model. The only way to surpass that is by writing this code:
# I do not want to do this, is there a better way?
self.measure = "someRandomvalue"
self.save
I am making this as virtual attribute only for throwing validations. Is there a better way of throwing validations? The form has other validations, I do not want the error for this validations to be shown differently just because it is not an attribute.
I want it to validated only when active record is saved via create and update action of the controller and not when it is being updated by some random method of model.
I have seen other developers in my team doing similar thing and was always curious about one thing - "What are you trying to achieve doing things the way you are doing?". You see, I am not sure if validators should be used for values that will not be serialized.
Anyways, you may try using format validator instead of presence, which worked in my team's case:
# Rails 3/4
validates :measure, format: { with: /^.+$/, allow_nil: true }
# Rails 2
validates_format_of :measure, :with => /^.+$/, :allow_nil => true
You may also try using allow_blank instead of allow_nil.
I would rather create a custom validator along the lines of validates_accessor_of for values that I know will never be serialized.
HTH

rails validation of uniqueness on update

I have the following validation rule on one of the models,
validates :reciept_num, :presence => true,
:numericality => { :only_integer => true },
:uniqueness => true,
:on => :update,
:if => "!status_id.nil?"`
Now, when I update the object using update_attributes method it gives me following error
reciept_num: has already been taken.
While updating the the object I'm not changing the reciept_num attribute? So, why does this validation fails on update?
If I'm not updating the value, it must be the old one and hence should pass validation. Am I missing something.
First off, validations don't run based on whether the attribute has changed or not (unless of course you ask for that explicitly). Everytime a record with a uniqueness validation saves and the validation can run (as defined by :on, :if, :unless options) it will check whether there are any instances other than itself with the value that is supposed to be unique.
Since you've got conditions on your validation, I imagine you could end up creating two instances with the same receipt num, but where both have a null status_id. Set the status_id column and the validation kicks into action and finds the other instances.
Another thing is that since your validation is on update only you could create multiple instances with the same receipt num, again trying to update the record would trigger the validation.
I'm only guessing at the precise scenarios though.

Rails validation problem

I've got a User model with three fields, :email, :display_name and :handle. Handle is created behind the scenes from the :display_name.
I'm using the following validations:
validates :display_name, :presence => :true, :uniqueness => { :message => "Sorry, another user has already chosen that name."}, :on => :update
validates :email, :presence => :true, :uniqueness => { :message => "An account with that email already exists." }
I use the handle as the to_param in the model. If the user fails the validation by submitting a :display_name that already exists, then tries to change it and resubmit the form, Rails seems to use the new handle as the validation for the email -- in other words, it assumes that the email doesn't belong to the current user and validation on the email then fails. At this point, Rails assumes that the changed display name/handle is the one to use for the look up and the update action can't complete at all, because it can't find the user based on the new handle.
Here's the update method:
def update
#user = User.find_by_handle(params[:id])
#handle = params[:user][:display_name]
#user.handle = #handle.parameterize
...
end
This problem doesn't happen when the validation first fails on a duplicate email, so I'm assuming it's something about the way I've written the update method -- maybe I should try setting the handle in the model?
maybe I should try setting the handle in the model?
^ This.
The controller isn't the place to do something like this. If it's model logic that's happening behind the scenes, beyond the user's control, why put it in controller code?
Do it instead in a before_save filter, which is guaranteed to run only after the chosen display name is determined to be available and the record is deemed valid. In this way the handle won't be changed on the cached record until it is actually committed to the db, eliminating the problem of the incorrectly generated URL.
before_save :generate_handle
...
def generate_handle
self.handle = display_name.parameterize
end

Validation only in specific form

Is there any way to trigger validation only in specific forms(controller's action), not globally at every save or update?
Something like User.create(:validate=>true) flag.
Yes, you can supply conditionals to the validations, eg:
validates_presence_of :something, :if => :special?
private
def make_sepcial
#special = true
end
def special?
#special
end
Now all you have to do to turn on these validations is:
s = SomeModel.new
s.make_special
As you explained in the comments, you want to skip validation for new records. In that case, you can use thomasfedb's answer, but don't use the #special variable, but:
validates_presence_of :something, :if => :persisted?
This will validate only for saved Users, but not for new Users. See the API documentation on persisted?.
This is a bit old. But I found http://apidock.com/rails/Object/with_options to be a good way of handling this sort of behaviour.

Resources