validate and update single attribute rails

后端 未结 5 1471
旧巷少年郎
旧巷少年郎 2021-02-08 22:55

I have the following in my user model

attr_accessible :avatar, :email

validates_presence_of :email
has_attached_file :avatar # paperclip

validates_attachment_s         


        
相关标签:
5条回答
  • 2021-02-08 23:11

    A condition?

    validates_presence_of :email, :if => :email_changed?
    
    0 讨论(0)
  • 2021-02-08 23:14

    You could validate the attribute by hand and use update_attribute, that skips validation. If you add this to your User:

    def self.valid_attribute?(attr, value)
      mock = self.new(attr => value)
      if mock.valid?
        true
      else
        !mock.errors.has_key?(attr)
      end
    end
    

    And then update the attribute thusly:

    if(!User.valid_attribute?('avatar', params[:user][:avatar])
        # Complain or whatever.
    end
    @user.update_attribute('avatar', params[:user][:avatar])
    

    You should get your single attribute updated while only (manually) validating that attribute.

    If you look at how Milan Novota's valid_attribute? works, you'll see that it performs the validations and then checks to see if the specific attr had issues; it doesn't matter if any of the other validations failed as valid_attribute? only looks at the validation failures for the attribute that you're interested in.

    If you're going to be doing a lot of this stuff then you could add a method to User:

    def update_just_this_one(attr, value)
        raise "Bad #{attr}" if(!User.valid_attribute?(attr, value))
        self.update_attribute(attr, value)
    end
    

    and use that to update your single attribute.

    0 讨论(0)
  • 2021-02-08 23:22

    Have you tried putting a condition on the validates_presence_of :email ?

    http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000083

    Configuration options:

    if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.

    unless - Specifies a method, proc or string to call to determine if the validation should not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The method, proc or string should return or evaluate to a true or false value.

    0 讨论(0)
  • 2021-02-08 23:25

    Here is my solution. It keeps the same behaviour than .valid? method, witch returns true or false, and add errors on the model on witch it was called.

    class MyModel < ActiveRecord::Base
      def valid_attributes?(attributes)
        mock = self.class.new(self.attributes)
        mock.valid?
        mock.errors.to_hash.select { |attribute| attributes.include? attribute }.each do |error_key, error_messages|
          error_messages.each do |error_message|
            self.errors.add(error_key, error_message)
          end
        end
    
        self.errors.to_hash.empty?
      end
    end
    
    > my_model.valid_attributes? [:first_name, :email] # => returns true if first_name and email is valid, returns false if at least one is not valid
    > my_modal.errors.messages # => now contain errors of the previous validation
    {'first_name' => ["can't be blank"]}
    
    0 讨论(0)
  • 2021-02-08 23:31

    I am assuming you need this, because you have a multi-step wizard, where you first upload the avatar and the e-mail is filled in later.

    To my knowledge, with your validations as they are, I see no good working solution. Either you validate all, or you update the avatar without validations. If it would be a simple attribute, you could check if the new value passes the validation seperately, and then update the model without validations (e.g. using update_attribute).

    I can suggest two possible alternative approaches:

    • either you make sure that the e-mail is always entered first, which I believe is not a bad solution. And then, with each save, the validation is met.
    • otherwise, change the validation. Why would you declare a validation on a model, if there are records in the database that do not meet the validation? That is very counter-intuitive.

    So I would propose something like this:

    validate :presence_of_email_after_upload_avatar
    
    def presence_of_email_after_upload_avatar
      # write some test, when the email should be present
      if avatar.present?
        errors.add(:email, "Email is required") unless email.present?
      end
    end
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题