Spec-Zone.ru › Ruby on Rails 7.0

модуль ActiveRecord::Validations::ClassMethods

Открытые методы экземпляров

validates_absence_of(*attr_names) Показать исходный код
# 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 для получения дополнительной информации.

validates_associated(*attr_names) Показать исходный код
# File activerecord/lib/active_record/validations/associated.rb, line 54
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.

validates_length_of(*attr_names) Показать исходный код
# 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 для получения дополнительной информации.

Также алиасирован как: validates_size_of
validates_numericality_of(*attr_names) Показать исходный код
# File activerecord/lib/active_record/validations/numericality.rb, line 30
def validates_numericality_of(*attr_names)
  validates_with NumericalityValidator, _merge_attributes(attr_names)
end

Проверяет, является ли значение указанного атрибута числовым, пытаясь преобразовать его в число с плавающей точкой с помощью Kernel.Float (если only_integer равно false или применяя его к регулярному выражению /\A[+\-]?\d+\z/ (если only_integer установлено в true). Точность Kernel.Float по умолчанию соответствует значению точности столбца или 15.

См. ActiveModel::Validations::HelperMethods.validates_numericality_of для получения дополнительной информации.

validates_presence_of(*attr_names) Показать исходный код
# 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! для получения дополнительной информации.

validates_size_of(*attr_names)
Псевдоним для: validates_length_of
validates_uniqueness_of(*attr_names) Показать исходный код
# File activerecord/lib/active_record/validations/uniqueness.rb, line 241
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

Или даже на нескольких параметрах scope. Например, чтобы убедиться, что учитель может быть в расписании только один раз за семестр для определённого класса.

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

Чтобы создать условия, основанные на состоянии записи, определите вызываемую функцию с параметром, который будет самой записью. Этот пример проверяет уникальность title для года публикации:

class Article < ActiveRecord::Base
  validates_uniqueness_of :title, conditions: ->(article) {
    published_at = article.published_at
    where(published_at: published_at.beginning_of_year..published_at.end_of_year)
  }
end

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

Параметры настройки:

  • :message — Указывает пользовательское сообщение об ошибке (по умолчанию: «уже используется»).

  • :scope — Один или несколько столбцов, по которым ограничить область действия условия уникальности.

  • :conditions — Указывает условия, которые будут включены как WHERE фрагмент SQL для ограничения поиска уникальности (например, conditions: -> { where(status: 'active') }).

  • :case_sensitive — Ищет точное совпадение. Не учитывается для столбцов, не являющихся текстовыми. По умолчанию учитывается значение collation базы данных.

  • :allow_nil — Если установлено в значение true, пропускает эту проверку, если атрибут nil (по умолчанию false).

  • :allow_blank — Если установлено в значение true, пропускает эту проверку, если атрибут пустой (по умолчанию false).

  • :if — Указывает метод, proc или строку для вызова, чтобы определить, должна ли выполняться проверка (например, if: :allow_validation, или if: Proc.new { |user| user.signup_step > 2 }). Метод, proc или строка должны возвращать или вычислять значение true или false.

  • :unless — Указывает метод, proc или строку для вызова, чтобы определить, не должна ли выполняться проверка (например, unless: :skip_validation, или unless: Proc.new { |user| user.signup_step <= 2 }). Метод, proc или строка должны возвращать или вычислять значение 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:

  • ActiveRecord::ConnectionAdapters::Mysql2Adapter.

  • ActiveRecord::ConnectionAdapters::SQLite3Adapter.

  • ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API