Spec-Zone.ru › Ruby on Rails 8.1

class ActiveRecord::Base

Родительский класс:
Object
Подключённые модули:
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::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])

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

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, если значение равно нулю.

Например, у пользователя User в Active Record с атрибутом 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_ также можно использовать несколько атрибутов, разделив их словом «and».

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 не нашёл ни одной записи. Либо строка с указанным идентификатором не существует, либо она не соответствует дополнительным ограничениям. Некоторые вызовы 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