Spec-Zone.ru › Ruby on Rails 6.1

class ActiveRecord::Base

Родитель:
Object
Включенные модули:
ActiveRecord::Core, ActiveRecord::Persistence, ActiveRecord::ReadonlyAttributes, ActiveRecord::ModelSchema, ActiveRecord::Inheritance, ActiveRecord::Scoping, ActiveRecord::Sanitization, ActiveRecord::AttributeAssignment, ActiveModel::Conversion, ActiveRecord::Integration, ActiveRecord::Validations, ActiveRecord::CounterCache, ActiveRecord::Attributes, ActiveRecord::Locking::Optimistic, ActiveRecord::Locking::Pessimistic, ActiveRecord::AttributeMethods, ActiveRecord::Callbacks, ActiveRecord::Timestamp, ActiveRecord::Associations, ActiveModel::SecurePassword, ActiveRecord::AutosaveAssociation, ActiveRecord::NestedAttributes, ActiveRecord::Transactions, ActiveRecord::NoTouching, ActiveRecord::Reflection, ActiveRecord::AttributeMethods::Serialization, ActiveRecord::Store, ActiveRecord::SecureToken, ActiveRecord::SignedId, ActiveRecord::Suppressor

Active Record

Объекты Active Record не указывают свои атрибуты напрямую, а вместо этого выводят их из определения таблицы, к которой они связаны. Добавление, удаление и изменение атрибутов и их типов выполняется непосредственно в базе данных. Любое изменение мгновенно отражается в объектах Active Record. Сопоставление, связывающее определенный класс Active Record с определенной таблицей базы данных, происходит автоматически в большинстве распространённых случаев, но может быть переопределено в редких случаях.

См. правила сопоставления в таблице table_name и полный пример в files/activerecord/README_rdoc.html для более подробного понимания.

Создание

Active Record принимает параметры конструктора либо в виде хэша, либо в виде блока. Метод хэша особенно полезен, когда вы получаете данные из другого источника, например, из HTTP-запроса. Он работает так:

user = User.new(name: "David", occupation: "Code Artist")
user.name # => "David"

Вы также можете использовать инициализацию с блоком:

user = User.new do |u|
  u.name = "David"
  u.occupation = "Code Artist"
end

И, конечно, вы можете просто создать объект и указать атрибуты позже:

user = User.new
user.name = "David"
user.occupation = "Code Artist"

Условие

Условие может быть указано в виде строки, массива или хэша, представляющего часть WHERE SQL-запроса. Формат массива следует использовать, когда входные данные условия являются небезопасными и требуют санизации. Формат строки можно использовать для заявлений, которые не содержат небезопасные данные. Формат хэша работает очень похоже на формат массива, за исключением того, что возможны только равенство и диапазон. Примеры:

class User < ActiveRecord::Base
  def self.authenticate_unsafely(user_name, password)
    where("user_name = '#{user_name}' AND password = '#{password}'").first
  end

  def self.authenticate_safely(user_name, password)
    where("user_name = ? AND password = ?", user_name, password).first
  end

  def self.authenticate_safely_simply(user_name, password)
    where(user_name: user_name, password: password).first
  end
end

Метод authenticate_unsafely вставляет параметры непосредственно в запрос и, следовательно, подвержен атакам SQL-инъекций, если параметры user_name и password получены непосредственно из HTTP-запроса. Методы authenticate_safely и authenticate_safely_simply обезопасят user_name и password перед их вставкой в запрос, что гарантирует, что злоумышленник не сможет обойти запрос и подделать вход (или что-то худшее).

При использовании нескольких параметров в условиях легко потерять представление о том, что представляет собой четвёртый или пятый знак вопроса. В таких случаях вы можете использовать именованные переменные связывания вместо них. Это делается путём замены знаков вопроса символами и предоставлением хэша со значениями для соответствующих символьных ключей:

Company.where(
  "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
  { id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }
).first

Аналогично, простой хэш без оператора сгенерирует условия на основе равенства с оператором SQL AND. Например:

Student.where(first_name: "Harvey", status: 1)
Student.where(params[:student])

Для использования оператора SQL BETWEEN можно использовать диапазон в хэше:

Student.where(grade: 9..12)

Для использования оператора SQL IN в хэше можно использовать массив:

Student.where(grade: [9,11,12])

При объединении таблиц для указания имени таблицы конкретного условия можно использовать вложенные хэши или ключи в формате 'имя_таблицы.имя_столбца'. Например:

Student.joins(:schools).where(schools: { category: 'public' })
Student.joins(:schools).where('schools.category' => 'public' )

Переопределение стандартных аксессоров

Все значения столбцов автоматически доступны через базовые аксессоры в объекте Active Record, но иногда вам нужно специализировать это поведение. Это можно сделать, переопределив стандартные аксессоры (используя то же имя, что и у атрибута) и вызвав super для фактического изменения.

class Song < ActiveRecord::Base
  # Uses an integer of seconds to hold the length of the song

  def length=(minutes)
    super(minutes.to_i * 60)
  end

  def length
    super / 60
  end
end

Методы запроса атрибутов

Помимо базовых аксессоров, методы запроса также автоматически доступны в объекте Active Record. Методы запроса позволяют проверить, присутствует ли значение атрибута. Кроме того, при работе с числовыми значениями метод запроса вернёт false, если значение равно нулю.

Например, у объекта Active Record User с атрибутом name есть метод name?, который вы можете вызвать, чтобы определить, есть ли у пользователя имя:

user = User.new(name: "David")
user.name? # => true

anonymous = User.new(name: "")
anonymous.name? # => false

Доступ к атрибутам до приведения их к типу

Иногда вам нужно прочитать данные атрибута в сыром виде, не дожидаясь приведения к типу, определяемого столбцом. Это можно сделать, используя аксессоры <attribute>_before_type_cast, которые есть у всех атрибутов. Например, если в модели Account есть атрибут balance, вы можете вызвать account.balance_before_type_cast или account.id_before_type_cast.

Это особенно полезно в ситуациях проверки, когда пользователь может ввести строку для целочисленного поля, и вы хотите отобразить исходную строку в сообщении об ошибке. Нормальный доступ к атрибуту приведёт строку к 0, что не нужно.

Динамические методы поиска по атрибутам

Динамические методы поиска по атрибутам — это слегка устаревший способ получения (и/или создания) объектов с помощью простых запросов без обращения к SQL. Они работают путём добавления имени атрибута к find_by_, как Person.find_by_user_name. Вместо написания Person.find_by(user_name: user_name), вы можете использовать Person.find_by_user_name(user_name).

Добавление восклицательного знака (!) в конец динамических методов поиска заставит их генерировать ошибку ActiveRecord::RecordNotFound, если они не возвращают записей, как Person.find_by_last_name!.

Возможно также использование нескольких атрибутов в одном find_by_, разделяя их словом «и».

Person.find_by(user_name: user_name, password: password)
Person.find_by_user_name_and_password(user_name, password) # with dynamic finder

Также возможно применение этих динамических методов поиска к отношениям и именованным областям.

Payment.order("created_on").find_by_amount(50)

Сохранение массивов, хэшей и других неотображаемых объектов в текстовых столбцах

Active Record может сериализовать любой объект в текстовых столбцах с использованием YAML. Для этого необходимо указать это с помощью вызова метода класса serialize. Это позволяет хранить массивы, хэши и другие неотображаемые объекты без дополнительных действий.

class User < ActiveRecord::Base
  serialize :preferences
end

user = User.create(preferences: { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }

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

class User < ActiveRecord::Base
  serialize :preferences, Hash
end

user = User.create(preferences: %w( one two three ))
User.find(user.id).preferences    # raises SerializationTypeMismatch

Когда вы указываете опцию класса, значение по умолчанию для этого атрибута будет новым экземпляром этого класса.

class User < ActiveRecord::Base
  serialize :preferences, OpenStruct
end

user = User.new
user.preferences.theme_color = "red"

Единотабличное наследование

Active Record позволяет реализовать наследование, сохраняя имя класса в столбце с именем «type» по умолчанию. См. ActiveRecord::Inheritance для более подробной информации.

Подключение к нескольким базам данных в разных моделях

Подключения обычно создаются через ActiveRecord::Base.establish_connection и извлекаются через ActiveRecord::Base.connection. Все классы, наследующие от ActiveRecord::Base, будут использовать это подключение. Но вы также можете установить подключение, специфичное для класса. Например, если Course — это ActiveRecord::Base, но находится в другой базе данных, вы можете просто сказать Course.establish_connection и Course, а также все его подклассы будут использовать это подключение вместо стандартного.

Эта функция реализована путём хранения пула подключений в ActiveRecord::Base, который представляет собой хэш, индексированный по классу. Если запрашивается подключение, метод ActiveRecord::Base.retrieve_connection будет подниматься по иерархии классов до тех пор, пока подключение не будет найдено в пуле подключений.

Исключения

  • ActiveRecordError — общий класс ошибок и суперкласс всех других ошибок, генерируемых Active Record.

  • AdapterNotSpecified — в хэше конфигурации, используемом в ActiveRecord::Base.establish_connection, отсутствовал ключ :adapter.

  • AdapterNotFound — ключ :adapter в ActiveRecord::Base.establish_connection указал на несуществующий адаптер (или неправильное написание существующего).

  • AssociationTypeMismatch — объект, назначенный ассоциации, не был того типа, который указан в определении ассоциации.

  • AttributeAssignmentError — произошла ошибка при выполнении массовой присваивания с помощью метода ActiveRecord::Base#attributes=. Вы можете проверить свойство attribute объекта исключения, чтобы определить, какой атрибут вызвал ошибку.

  • ConnectionNotEstablished — подключение не установлено. Используйте ActiveRecord::Base.establish_connection перед выполнением запроса.

  • MultiparameterAssignmentErrors — коллекция ошибок, возникших во время массового присваивания с помощью метода ActiveRecord::Base#attributes=. Свойство errors этого исключения содержит массив объектов AttributeAssignmentError, которые необходимо проверить, чтобы определить, какие атрибуты вызвали ошибки.

  • RecordInvalid — генерируется методами ActiveRecord::Base#save! и ActiveRecord::Base.create!, когда запись некорректна.

  • RecordNotFound — ни одна запись не ответила на метод ActiveRecord::Base.find. Либо строка с заданным ID не существует, либо строка не удовлетворяет дополнительным ограничениям. Некоторые вызовы ActiveRecord::Base.find не генерируют это исключение для обозначения того, что ничего не найдено, пожалуйста, проверьте документацию для получения более подробной информации.

  • SerializationTypeMismatch — сериализованный объект не был того класса, который указан как второй параметр.

  • StatementInvalid — сервер базы данных отклонил SQL-запрос. Точная ошибка указана в сообщении.

Примечание: перечисленные атрибуты являются атрибутами на уровне класса (доступны как на уровне класса, так и на уровне экземпляра). Поэтому возможно назначить логгер классу через Base.logger=, который затем будет использован всеми экземплярами в текущем пространстве объектов.

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

Spec-Zone.ru

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