модуль ActiveRecord::Validations::ClassMethods
Публичные методы экземпляров
# File activerecord/lib/active_record/validations/absence.rb, line 20 def validates_absence_of(*attr_names) validates_with AbsenceValidator, _merge_attributes(attr_names) end
Проверяет, что указанные атрибуты отсутствуют (как определено методом Object#present?). Если атрибут является ассоциацией, связанный объект считается отсутствующим, если он был помечен для удаления.
См. ActiveModel::Validations::HelperMethods#validates_absence_of для получения дополнительной информации.
# File activerecord/lib/active_record/validations/associated.rb, line 55 def validates_associated(*attr_names) validates_with AssociatedValidator, _merge_attributes(attr_names) end
Проверяет, являются ли связанные объекты или объекты валидными. Работает с любым типом ассоциации.
class Book < ActiveRecord::Base has_many :pages belongs_to :library validates_associated :pages, :library end
ПРЕДУПРЕЖДЕНИЕ: Эта проверка не должна использоваться с обеих сторон ассоциации. Это приведёт к циклической зависимости и бесконечной рекурсии.
ПРИМЕЧАНИЕ: Эта проверка не завершится с ошибкой, если ассоциация не была назначена. Если вы хотите убедиться, что ассоциация присутствует и гарантированно валидна, вы также должны использовать validates_presence_of.
Параметры конфигурации:
-
:message- Пользовательское сообщение об ошибке (по умолчанию: «недействителен»). -
:on- Определяет контексты, в которых эта проверка активна. По умолчанию выполняется во всех контекстах проверкиnil. Можно передать символ или массив символов. (например,on: :createилиon: :custom_validation_contextилиon: [:create, :custom_validation_context]) -
:if- Определяет метод, лямбда-выражение или строку для вызова, чтобы определить, должна ли выполняться проверка (например,if: :allow_validation, илиif: Proc.new { |user| user.signup_step > 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение. -
:unless- Определяет метод, лямбда-выражение или строку для вызова, чтобы определить, не должна ли выполняться проверка (например,unless: :skip_validation, илиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение.
# File activerecord/lib/active_record/validations/length.rb, line 19 def validates_length_of(*attr_names) validates_with LengthValidator, _merge_attributes(attr_names) end
Проверяет, что указанные атрибуты соответствуют ограничениям длины. Если атрибут является ассоциацией, записи, помеченные для удаления, не учитываются.
См. ActiveModel::Validations::HelperMethods#validates_length_of для получения дополнительной информации.
# File activerecord/lib/active_record/validations/presence.rb, line 63 def validates_presence_of(*attr_names) validates_with PresenceValidator, _merge_attributes(attr_names) end
Проверяет, что указанные атрибуты не пустые (как определено методом Object#blank?), и, если атрибут является ассоциацией, что связанный объект не помечен для удаления. Происходит по умолчанию при сохранении.
class Person < ActiveRecord::Base has_one :face validates_presence_of :face end
Атрибут face должен присутствовать в объекте и не должен быть пустым или помеченным для удаления.
Если нужно проверить наличие булевого поля (где реальными значениями являются true и false), используйте validates_inclusion_of :field_name, in: [true, false].
Это из-за того, как Object#blank? обрабатывает булевы значения: false.blank? # => true.
Данный валидатор делегирует проверку наличия Active Model, добавляя проверку, чтобы связанный объект не был помечен для удаления. Это предотвращает успешную валидацию и сохранение родительского объекта, после чего связанный объект удаляется, тем самым приводя родительский объект в невалидное состояние.
ПРИМЕЧАНИЕ: Эта проверка не завершится с ошибкой, когда используется с ассоциацией, если последняя была назначена, но не валидна. Если вы хотите убедиться, что она присутствует и валидна, вам также нужно использовать validates_associated.
Параметры конфигурации:
-
:message- Пользовательское сообщение об ошибке (по умолчанию: «не может быть пустым»). -
:on- Определяет контексты, в которых эта проверка активна. По умолчанию выполняется во всех контекстах проверкиnil. Можно передать символ или массив символов. (например,on: :createилиon: :custom_validation_contextилиon: [:create, :custom_validation_context]) -
:if- Определяет метод, лямбда-выражение или строку для вызова, чтобы определить, должна ли выполняться проверка (например,if: :allow_validation, илиif: Proc.new { |user| user.signup_step > 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение. -
:unless- Определяет метод, лямбда-выражение или строку для вызова, чтобы определить, не должна ли выполняться проверка (например,unless: :skip_validation, илиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение. -
:strict- Определяет, должна ли проверка быть строгой. См. ActiveModel::Validations#validates! для получения дополнительной информации.
# File activerecord/lib/active_record/validations/uniqueness.rb, line 221 def validates_uniqueness_of(*attr_names) validates_with UniquenessValidator, _merge_attributes(attr_names) end
Проверяет, являются ли значения указанных атрибутов уникальными во всей системе. Полезно для проверки, чтобы только один пользователь мог иметь имя «davidhh».
class Person < ActiveRecord::Base validates_uniqueness_of :user_name end
Также можно проверить, являются ли значения указанных атрибутов уникальными на основе параметра :scope:
class Person < ActiveRecord::Base validates_uniqueness_of :user_name, scope: :account_id end
Или даже нескольких параметров области. Например, чтобы убедиться, что учитель может быть в расписании только один раз за семестр для определенного класса.
class TeacherSchedule < ActiveRecord::Base validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id] end
Также возможно ограничить условие уникальности набором записей, соответствующих определённым условиям. В этом примере при проверке уникальности заголовка статьи не учитываются архивные статьи:
class Article < ActiveRecord::Base
validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
end
При создании записи выполняется проверка, чтобы в базе данных не существовала запись с заданным значением для указанного атрибута (который отображается в столбце). При обновлении записи выполняется та же проверка, но без учёта самой записи.
Параметры конфигурации:
-
:message— Указывает пользовательское сообщение об ошибке (по умолчанию: «уже используется»). -
:scope— Один или несколько столбцов, по которым следует ограничить область условия уникальности. -
:conditions— Укажите условия, которые должны быть включены как фрагмент SQLWHEREдля ограничения поиска уникальности (например,conditions: -> { where(status: 'active') }). -
:case_sensitive— Ищет точное совпадение. Игнорируется для столбцов, не являющихся текстовыми (trueпо умолчанию). -
:allow_nil— Если установлено значениеtrue, эта проверка пропускается, если атрибутnil(по умолчаниюfalse). -
:allow_blank— Если установлено значениеtrue, эта проверка пропускается, если атрибут пустой (по умолчаниюfalse). -
:if— Указывает метод, лямбда-выражение или строку для вызова, чтобы определить, должна ли выполняться проверка (например,if: :allow_validation, илиif: Proc.new { |user| user.signup_step > 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение. -
:unless— Указывает метод, лямбда-выражение или строку для вызова, чтобы определить, не должна ли выполняться проверка (например,unless: :skip_validation, илиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, лямбда-выражение или строка должны возвращать или оцениваться какtrueилиfalseзначение.
Конфликты и целостность
Использование этого метода валидации вместе с ActiveRecord::Base#save не гарантирует отсутствие дублирующих вставленных записей, потому что проверки уникальности на уровне приложения потенциально подвержены конфликтным ситуациям. Например, предположим, что два пользователя пытаются одновременно опубликовать комментарий, а заголовок комментария должен быть уникальным. На уровне базы данных действия этих пользователей могут чередоваться следующим образом:
User 1 | User 2
------------------------------------+--------------------------------------
# User 1 checks whether there's |
# already a comment with the title |
# 'My Post'. This is not the case. |
SELECT * FROM comments |
WHERE title = 'My Post' |
|
| # User 2 does the same thing and also
| # infers that their title is unique.
| SELECT * FROM comments
| WHERE title = 'My Post'
|
# User 1 inserts their comment. |
INSERT INTO comments |
(title, content) VALUES |
('My Post', 'hi!') |
|
| # User 2 does the same thing.
| INSERT INTO comments
| (title, content) VALUES
| ('My Post', 'hello!')
|
| # ^^^^^^
| # Boom! We now have a duplicate
| # title! Лучший способ решения этой проблемы — добавить уникальный индекс в таблицу базы данных с помощью connection.add_index. В редком случае возникновения конфликтной ситуации база данных гарантирует уникальность поля.
Когда база данных обнаружит такую дублирующую вставку, ActiveRecord::Base#save вызовет исключение ActiveRecord::StatementInvalid. Вы можете либо позволить этой ошибке распространиться (что приведёт к отображению стандартной страницы ошибок Rails), либо перехватить её и перезапустить транзакцию (например, сообщив пользователю, что заголовок уже существует, и попросив повторно ввести заголовок). Этот метод также известен как оптимистический контроль конкурентности.
Входящие в комплект адаптеры ActiveRecord::ConnectionAdapters отличают ошибки уникальных индексов от других типов баз данных, выбрасывая исключение ActiveRecord::RecordNotUnique. Для других адаптеров вам необходимо будет проанализировать сообщение об ошибке (специфичное для базы данных) для определения такой ситуации.
Следующие адаптеры, включённые в комплект, выбрасывают исключение ActiveRecord::RecordNotUnique:
© 2004–2019 David Heinemeier Hansson
Licensed under the MIT License.