简体   繁体   中英

How to add ruby on rails model validation for two fields?

#  columns
#  maximum_users           :integer(4)
#  last_registration_date  :datetime

class Training < ApplicationRecord

 validates :maximum_users, numericality: { greater_than: 0, less_than_or_equal_to: 999,  
            only_integer: true }

 validate :last_reg_date_cannot_be_in_the_past



   private
   def last_reg_date_cannot_be_in_the_past
    if last_registration_date_changed? && last_registration_date < Time.now.utc
      errors.add(:training, "can't be in the past")
    end
  end

end

In the above model I need to add followings validations:

  1. If user selects any one of the field than other should be nil ( maximum_users or last_registration_date ).
  2. User cannot add both the fields values.
  3. User can choose not to select any of the values. But if he chooses then, anyone field must be allowed.
  4. maximum_users allowed is less than 999 .
  5. last_registration_date must be greater than today's date.

I have added few validations but don't think this is a good practice.

How can I refactor the model to cover all the above scenarios ?

Having plenty of validations is not a bad practice. At some point you may want to move it to a validator class.

class Training < ApplicationRecord
 validates_with TrainingValidator
end

One problem I see with what you have is that last_registration_date can be set and maximum_users be nil, but your maximum_users validation above might not allow nil.

app/models/concerns/traning_validator.rb

classTrainingValidator < ActiveModel::Validator 

  def validate(training)
    if training.last_registration_date_changed? && training.last_registration_date < Time.now.utc
      training.errors.add(:training, "can't be in the past")
    end

    unless training.last_registration_date.nil? || training.maximum_users.nil?
      training.errors.add(:last_registration_date, "last_registration_date cannot both be set when maximum_users is set")
    end

    unless training.maximum_users.nil? || training.maximum_users < 999
      training.errors.add(:maximum_users, "must be less than 999")
    end
  end
end

I did something like this :

class NotInPastValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors.add attribute, (options[:message] || "Date can't be in the past") if value <= Time.now.utc
  end
end

Model :

 validates :maximum_users,
            numericality: {
              greater_than: 0,
              less_than_or_equal_to: 999,
              only_integer: true
            }
  validates :last_registration_date, not_in_past: true
  validate :validate_max_users_and_last_reg_date


  def validate_max_users_and_last_reg_date
    if maximum_users_changed? && last_registration_date_changed?
      errors.add(:training, 'Specify either maximum users or last registration date, not both')
    end
  end

  def maximum_users_changed?
    changes.keys.include? 'maximum_users'
  end

  def last_registration_date_changed?
    changes.keys.include? 'last_registration_date'
  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