ActiveRecord: убедитесь, что только одна запись имеет определенное значение атрибута? - PullRequest
0 голосов
/ 20 января 2011

У меня есть таблица Рас с множеством рас в разных штатах. Но мне нужно убедиться, что только одна раса помечена как current = true . Вот что я использовал при проверке модели гонки.

# current: boolean
validate :only_one_current

private
def only_one_current
  if self.current && (Race.current_race.id != self.id)
    errors.add(:base, "Races can have only one current race")
  end
end

Кажется, это работает большую часть времени, но иногда это не так, и я не уверен, почему. Если он не работает, он запрещает сохранение новой записи с current = t сразу после удаления другой текущей записи. Я думаю, что это связано с постоянством AR.

Должен быть лучший способ сделать это?

Ответы [ 2 ]

5 голосов
/ 20 февраля 2011

Ваша проблема на самом деле выходит за рамки ActiveRecord. Независимо от того, как вы реализуете свой метод before_save, всегда будет возможно возникновение состояния гонки (без каламбура) и двух записей, имеющих current = true в базе данных. Для получения дополнительной информации см. Раздел Параллельность и целостность для validates_uniqueness_of .

Основная проблема заключается в том, что логика проверки того, имеет ли запись current = true и операция, устанавливающая запись в current = true, не является атомарной. Эта проблема часто возникает в параллельных системах.

Чтобы решить эту проблему, вам необходим индекс уникального ключа в базе данных. Я бы порекомендовал изменить текущий флаг на приоритетное поле. Приоритетом является целое число, которое имеет уникальный индекс ключа. База данных гарантирует, что не существует двух записей одновременно с одинаковым значением приоритета. «Текущей» расой всегда будет та, которая имеет наивысшее значение приоритета.

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

0 голосов
/ 20 января 2011

Вы должны называть это как before_save, а не как валидатор:

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