рельсы 5 проверить количество в соответствующей области - PullRequest
0 голосов
/ 27 августа 2018

У меня есть модель с именем Person и модель с именем Contract .A Лицо has_many Контракты и Контракт принадлежит a Лицо .

A Контракт имеет start_date и end_date .Контракт считается активным , если текущая дата находится в промежутке между.

У меня есть область действия по Контракту, называемая " active ", которая возвращаетзаписи соответственно.Человек может иметь любое количество неактивных контрактов, но должен иметь только один активный контракт.

Я пытаюсь найти лучший способ добавить проверку, которая предотвращает любой из этих случаев:

  • Создайте новый активный Контракт со связанным лицом, у которого уже есть активный контракт.
  • Измените связанное лицо в существующем активном контракте на лицо, которое уже имеет активный контракт.
  • Измените Контракт, который не активен, чтобы иметь дату start_date или end_date, которая делает его активным, когда связанное Лицо уже имеет активный Контракт.
  • Создайте Лицо, с которым связано несколько активных Контрактов.

Это то, чем я сейчас занимаюсь, и похоже, что это работает:

class Contract < ApplicationRecord
  belongs_to :person
  validates_uniqueness_of :person_id, conditions: -> { active }
  scope :active, -> { where("start_date <= ? AND end_date >= ?", Date.today, Date.today) }
end

Для меня это похоже на хак.Я не обеспокоен уникальностью, я обеспокоен размером;бывает так, что уникальность работает.Что если я хотел бы разрешить не более 2 активных контрактов?

Кроме того, ошибка проверки, которую я получаю, когда пытаюсь добавить несколько активных контрактов, говорит: «Человек уже занят», что вводит в заблуждение.Конечно, я могу добавить собственное сообщение, но это похоже на еще одно указание на то, что я делаю это неправильно.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Если я правильно понимаю полный сценарий, вам следует не только проверять уникальность -> активно.Иначе, когда ваш активный контракт становится неактивным - могут быть два неактивных контракта, которые теперь становятся активными.И я согласен, что проверка должна произойти с человеком (который должен даже решить вашу проблему с изменением отношений).

Так что я думаю, что вы хотите больше что-то вроде:

class Person < ApplicationRecord
  validates :contracts, :not_overlapping
end

class NotOverlappingValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return if value.blank?
    mark_overlapping_error to_ranges(value)
  end

  def mark_overlapping_error(ranges)
    ranges[0..-2].each_with_index do |range, index|
      # check if successive contracts are overlapping in their active interval
      next unless range.overlaps?(ranges[index + 1])
      # add some custom error about the overlapping
      return record.errors.add attribute, :overlaps 
    end
  end

  def to_ranges(contracts)
    # mapping contracts to their activity-interval
    value.sort_by(&:start_date).map do |contract|
      (contract.start_date..contract.end_date)
    end
  end
end

Таким образом, выс самого начала убедиться, что человек никогда не получит перекрывающиеся контракты.

0 голосов
/ 27 августа 2018

Для сообщения с ошибкой уникальности вы всегда можете настроить сообщения для проверок

validates :person_id, uniqueness: {scope: :active, message: 'Some custom message'}

Для нестандартных проверок вам нужен пользовательский валидатор или пользовательский метод проверки https://guides.rubyonrails.org/active_record_validations.html#custom-methods. Вам просто нужнодобавить ошибку для атрибута (или: base, если он не связан с атрибутом), когда возникает какое-либо условие, условие может проверять даты, пользователей и т. д.

class Contract < ApplicationRecord
  validate :allow_only_two_active_contracts

private
  def allow_only_two_active_contracts
    person_contracts = person.contracts.active.where.not(id: self.id).count #count all active contracts of the person except this one, not sure if the where.not is necessary

    errors.add(:person_id, 'This person already has two active contracts') if person_contracts >= 2
  end
end

То, что ActiveRecord пытается проверить ваш контракт, будетвызовите этот метод и после всех проверок, если errors имеет что-либо, запись недействительна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...