модуль ActiveRecord::Validations::ClassMethods
Открытые методы экземпляров
Создаёт объект так же, как Base.create, но вызывает save! вместо save, поэтому при невалидности записи будет поднято исключение.
# File activerecord/lib/active_record/validations.rb, line 35
def create!(attributes = nil, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr, &block) }
else
object = new(attributes)
yield(object) if block_given?
object.save!
object
end
end Проверяет, что ассоциированные объекты или объекты, являются валидными. Работает с любым типом ассоциации.
class Book < ActiveRecord::Base has_many :pages belongs_to :library validates_associated :pages, :library end
ПРЕДУПРЕЖДЕНИЕ: Эта валидация не должна использоваться на обеих сторонах ассоциации. Это приведёт к циклической зависимости и бесконечной рекурсии.
ПРИМЕЧАНИЕ: Эта валидация не будет выполняться, если ассоциация не назначена. Если вы хотите убедиться, что ассоциация присутствует и гарантированно валидна, вам также необходимо использовать validates_presence_of.
Параметры настройки:
-
:message- Пользовательское сообщение об ошибке (по умолчанию: «невалиден»). -
:on- Указывает, когда эта валидация активна. По умолчанию выполняется во всех контекстах валидации (nil), другие варианты —:createи:update. -
: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/associated.rb, line 44 def validates_associated(*attr_names) validates_with AssociatedValidator, _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.
Этот валидатор делегирует валидацию присутствия ActiveModel, добавляя проверку, что ассоциированный объект не помечен на удаление. Это предотвращает успешную валидацию и сохранение родительского объекта, что затем удаляет ассоциированный объект, тем самым приводя родительский объект в невалидное состояние.
Параметры настройки:
-
:message- Пользовательское сообщение об ошибке (по умолчанию: «не может быть пустым»). -
:on- Указывает, когда эта валидация активна. По умолчанию выполняется во всех контекстах валидации (nil), другие варианты —:createи:update. -
: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::Validation#validates!для получения дополнительной информации.
# File activerecord/lib/active_record/validations/presence.rb, line 60 def validates_presence_of(*attr_names) validates_with PresenceValidator, _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
Также можно ограничить условие уникальности набором записей, соответствующих определенным условиям. В этом примере архивированные статьи не учитываются при валидации уникальности атрибута title:
class Article < ActiveRecord::Base
validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
end
При создании записи выполняется проверка, чтобы в базе данных не существовало записи с заданным значением для указанного атрибута (который соответствует столбцу). При обновлении записи выполняется та же проверка, но без учёта самой записи.
Параметры настройки:
-
:message- Пользовательское сообщение об ошибке (по умолчанию: «уже используется»). -
:scope- Один или несколько столбцов, по которым нужно ограничить область условия уникальности. -
:conditions- Укажите условия, которые следует включить какWHEREфрагмент SQL для ограничения поиска уникальности (например,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::Validations#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! Это может произойти даже если вы используете транзакции с изоляцией уровня «сериализуемый». Лучший способ обойти эту проблему — добавить уникальный индекс в таблицу базы данных с помощью ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. В редких случаях возникновения проблемы гонки база данных гарантирует уникальность поля.
Когда база данных обнаруживает такую дублирующую вставку, ActiveRecord::Validations#save сгенерирует исключение ActiveRecord::StatementInvalid. Вы можете либо позволить этой ошибке распространиться (что приведёт к отображению стандартной страницы ошибок Rails), либо перехватить её и перезапустить транзакцию (например, сообщив пользователю, что заголовок уже существует, и попросив их повторно ввести заголовок). Этот метод также известен как оптимистичный контроль конкурентности.
Входящие в комплект адаптеры ActiveRecord::ConnectionAdapters отличают ошибки уникального индекса от других типов ошибок базы данных, генерируя исключение ActiveRecord::RecordNotUnique. Для других адаптеров придётся проанализировать сообщение об ошибке (специфичное для базы данных) для определения такого случая.
Следующие встроенные адаптеры генерируют исключение ActiveRecord::RecordNotUnique:
# File activerecord/lib/active_record/validations/uniqueness.rb, line 238 def validates_uniqueness_of(*attr_names) validates_with UniquenessValidator, _merge_attributes(attr_names) end
© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.