Spec-Zone.ru › Ruby on Rails 5.2

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::DefineCallbacks, ActiveRecord::AttributeMethods, ActiveRecord::Callbacks, ActiveRecord::Timestamp, ActiveRecord::Associations, ActiveModel::SecurePassword, ActiveRecord::AutosaveAssociation, ActiveRecord::NestedAttributes, ActiveRecord::Aggregations, ActiveRecord::Transactions, ActiveRecord::TouchLater, ActiveRecord::NoTouching, ActiveRecord::Reflection, ActiveRecord::AttributeMethods::Serialization, ActiveRecord::Store, ActiveRecord::SecureToken, 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–2018 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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