класс 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
Методы запроса также будут учитывать любые переопределения стандартных аксессоров:
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.