Spec-Zone.ru › Ruby on Rails 4.1

класс ActiveRecord::Base

Родитель:
Объект
Включенные модули:
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::Locking::Optimistic, ActiveRecord::Locking::Pessimistic, ActiveRecord::AttributeMethods, ActiveRecord::Callbacks, ActiveRecord::Timestamp, ActiveRecord::Associations, ActiveModel::SecurePassword, ActiveRecord::AutosaveAssociation, ActiveRecord::NestedAttributes, ActiveRecord::Aggregations, ActiveRecord::Transactions, ActiveRecord::NoTouching, ActiveRecord::Reflection, ActiveRecord::AttributeMethods::Serialization, ActiveRecord::Store

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, но иногда вам нужно специализировать это поведение. Это можно сделать, переопределив стандартные аксессоры (используя то же имя, что и атрибут) и вызвав read_attribute(attr_name) и write_attribute(attr_name, value) для фактического изменения.

class Song < ActiveRecord::Base
  # Uses an integer of seconds to hold the length of the song

  def length=(minutes)
    write_attribute(:length, minutes.to_i * 60)
  end

  def length
    read_attribute(:length) / 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» (может быть изменён путём переопределения Base.inheritance_column). Это означает, что наследование, выглядящее так:

class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end

При выполнении Firm.create(name: "37signals"), эта запись будет сохранена в таблице companies с типом = «Firm». Затем вы можете снова получить эту строку, используя Company.where(name: '37signals').first , и она вернёт объект Firm.

Если у вас нет столбца типа, определённого в вашей таблице, наследование по одной таблице не будет выполнено. В этом случае оно будет работать как обычное подклассирование без специальной магии для различения между ними или перезагрузки правильного типа с помощью find.

Обратите внимание, что все атрибуты для всех случаев сохраняются в одной таблице. Подробнее: www.martinfowler.com/eaaCatalog/singleTableInheritance.html

Подключение к нескольким базам данных в разных моделях

Подключения обычно создаются через 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–2016 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API