Spec-Zone.ru › Ruby on Rails 7.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::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])

При объединении таблиц вложенные хэши или ключи в формате '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, если значение равно нулю.

Например, у объекта 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.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