简体   繁体   中英

validates combination of attributes

I have two attributes (hours and days) in my model (auction). I have business logic on the combination of hours and days. For example

auction duration = days*24 + hours

I also have some basic validation on hours and days:

class Auction < ActiveRecord::Base
  validates :days,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }
  validates :hours,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }

I would like to incorporate my business logic into the validation, such that hour and days cannot both be zero. Is there a way to do this with an ActiveRecord validation? I know there are other ways to do this w/out an AR validation. Right now I am creating a new instance of my model. Validating days and hours as above. Then I have a model method that "manually" does the validation and removes the instance if it doesn't pass. I know this is not the best way to do this

  def compute_end_time
    # assumes that days and hours are already valid
    time = self.days*24 + self.hours

    if time > 1
      self.end_time =  time.hours.from_now.utc
      self.save

    else
      # Auction duration too short (i.e. zero)
      self.delete
    end
  end

You need to write a private validate function something like this.

class Auction < ActiveRecord::Base
   validates :days,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }
   validates :hours,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }

   validate :days_and_hours

   private

   def days_and_hours
     if !(days && hours)
     errors.add_to_base("Days and hours can not be zero. Thank you.")
   end
 end
end

You can check for a value greater than 0 by using the numericality validator:

validates :auction_duration, :numericality => { :greater_than => 0 }

More info is at: http://guides.rubyonrails.org/active_record_validations_callbacks.html#numericality

So my final solution was an extension of @rharrison33:

  validates :days,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }
  validates :hours,:presence => true, :numericality => { :greater_than_or_equal_to => 0, :only_integer => true }
  validate :days_and_hours

def days_and_hours
    # It may not validate days and hours separately before validating days_and_hours,
    # so I need to make sure that days and hours are both not NIL before trying to compute 'time'
    # I also only want to compute end_time if it has not been computed yet
    if (!self.days.nil? && !self.hours.nil?)

      if (self.end_time.nil?) # if end_time has not yet been computed

        if (self.days == 0 && self.hours == 0)
          self.errors[:base] << "Days and hours can not be zero."
        else
          time =  self.hours  + self.days*24
          self.end_time = time.minutes.from_now.utc
          self.setStatus # validate status after end time is set
        end
      end
    end
  end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM