класс 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
В качестве альтернативы можно использовать self[:attribute]=(value) и self[:attribute] или write_attribute(:attribute,
value) и read_attribute(:attribute).
Методы запроса атрибутов
В дополнение к базовым аксессорам методы запроса также автоматически доступны в объекте Active Record. Методы запроса позволяют проверить наличие значения атрибута. Для числовых значений наличие определяется как ненулевое значение.
Например, у объекта 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!.
Также можно использовать несколько атрибутов в одном поиске, разделяя их «и».
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, который является хэшем, индексированным по классу. Если запрашивается подключение, метод retrieve_connection будет подниматься по иерархии классов до тех пор, пока подключение не будет найдено в пуле подключений.
Исключения
-
ActiveRecordError — общий класс ошибок и суперкласс всех других ошибок, генерируемых Active Record.
-
AdapterNotSpecified — хэш конфигурации, используемый в
establish_connection, не содержал ключа:adapter. -
AdapterNotFound — ключ
:adapterвestablish_connectionуказал на несуществующий адаптер (или неправильное написание существующего). -
AssociationTypeMismatch — объект, назначенный ассоциации, не соответствовал типу, указанному в определении ассоциации.
-
AttributeAssignmentError — ошибка при массовом назначении атрибутов с помощью метода
attributes=. Вы можете проверить свойствоattributeобъекта исключения, чтобы определить, какой атрибут вызвал ошибку. -
ConnectionNotEstablished — подключение не установлено. Используйте
establish_connectionперед выполнением запроса. -
MultiparameterAssignmentErrors — набор ошибок, возникших во время массового назначения с помощью метода
attributes=. Свойствоerrorsэтого исключения содержит массив объектов AttributeAssignmentError, которые нужно проверить, чтобы определить, какие атрибуты вызвали ошибки. -
RecordInvalid — генерируется save! и create! при невалидном объекте.
-
RecordNotFound — ни один объект не ответил на метод
find. Либо запись с заданным ID не существует, либо запись не соответствовала дополнительным ограничениям. Некоторые вызовыfindне генерируют эту ошибку, чтобы указать на отсутствие результатов; обратитесь к документации, чтобы узнать подробности. -
SerializationTypeMismatch — сериализованный объект не был того класса, который был указан во втором параметре.
-
StatementInvalid — сервер базы данных отклонил SQL-запрос. Точная ошибка добавлена в сообщение.
Примечание: перечисленные атрибуты являются атрибутами уровня класса (доступ к ним возможен как с уровня класса, так и с уровня экземпляра). Таким образом, возможно назначить логгер классу через Base.logger=, который затем будет использоваться всеми экземплярами в текущем пространстве объектов.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.