Spec-Zone.ru › Ruby on Rails 7.0

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, ActiveRecord::Encryption::EncryptableRecord

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

Методы запроса также будут учитывать любые переопределения стандартных аксессоров:

class User
  # Has admin boolean column
  def admin
    false
  end
end

user.update(admin: true)

user.read_attribute(:admin)  # => true, gets the column value
user[:admin] # => true, also gets the column value

user.admin   # => false, due to the getter overwrite
user.admin?  # => false, due to the getter overwrite

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

Иногда необходимо прочитать данные атрибута в сыром виде, не дожидаясь приведения к типу, определённого столбцом. Это можно сделать с помощью аксессоров <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–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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