Spec-Zone.ru › Ruby on Rails 7.2

класс ActiveRecord::Base

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

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 override
user.admin?  # => false, due to the getter override

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

Иногда необходимо прочитать исходные данные атрибута, не дожидаясь завершения преобразования типов, определённых столбцом. Это можно сделать с помощью аксессоров <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.lease_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