class ActiveRecord::Base
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–2019 David Heinemeier Hansson
Licensed under the MIT License.