Spec-Zone.ru › Ruby on Rails 8.1

module 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 60
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 — пользовательское сообщение об ошибке (по умолчанию: «is invalid»).

  • :on — задаёт контексты, в которых активна эта проверка. По умолчанию выполняется во всех контекстах проверки nil. Можно передать символ или массив символов (например, on: :create, on: :custom_validation_context или on: [:create, :custom_validation_context]).

  • :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.

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 31
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 40
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 должен присутствовать в объекте и не может быть пустым или помеченным для уничтожения.

Этот валидатор делегирует проверку наличия Active Model, добавляя проверку того, что связанный объект не помечен для уничтожения. Это не позволяет родительскому объекту успешно пройти проверку и сохраниться, после чего связанный объект будет удалён, а родительский объект окажется в недопустимом состоянии.

Дополнительные сведения см. в ActiveModel::Validations::HelperMethods.validates_presence_of.

ПРИМЕЧАНИЕ: При использовании этой проверки со связью она не завершится ошибкой, если связь была назначена, но недопустима. Если необходимо убедиться, что связь существует и является допустимой, нужно также использовать validates_associated.

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

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

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 — задаёт пользовательское сообщение об ошибке (по умолчанию: «has already been taken»).

  • :scope — один или несколько столбцов, ограничивающих область действия ограничения уникальности.

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

  • :case_sensitive — выполняет поиск точного совпадения. Игнорируется для нетекстовых столбцов. По умолчанию учитывается используемая базой данных сортировка.

  • :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::TrilogyAdapter.

  • ActiveRecord::ConnectionAdapters::SQLite3Adapter.

  • ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.

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

Spec-Zone.ru

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