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–2020 David Heinemeier Hansson
Licensed under the MIT License.