Spec-Zone.ru › Ruby on Rails 8.0

модуль ActiveRecord::Associations::ClassMethods

Ассоциации Active Record

Ассоциации — это набор макроподобных методов класса для связывания объектов через внешние ключи. Они выражают отношения, такие как «Проект имеет одного менеджера проекта» или «Проект принадлежит портфолио». Каждый макрос добавляет ряд методов в класс, которые специализируются в соответствии с символом коллекции или ассоциации и хэш-опциями. Он работает очень похожим образом на собственные методы Ruby attr*.

class Project < ActiveRecord::Base
  belongs_to              :portfolio
  has_one                 :project_manager
  has_many                :milestones
  has_and_belongs_to_many :categories
end

Класс проекта теперь имеет следующие методы (и другие) для облегчения навигации и управления его отношениями:

project = Project.first
project.portfolio
project.portfolio = Portfolio.first
project.reload_portfolio

project.project_manager
project.project_manager = ProjectManager.first
project.reload_project_manager

project.milestones.empty?
project.milestones.size
project.milestones
project.milestones << Milestone.first
project.milestones.delete(Milestone.first)
project.milestones.destroy(Milestone.first)
project.milestones.find(Milestone.first.id)
project.milestones.build
project.milestones.create

project.categories.empty?
project.categories.size
project.categories
project.categories << Category.first
project.categories.delete(category1)
project.categories.destroy(category1)

Предостережение

Не создавайте ассоциаций с теми же именами, что и методы экземпляров ActiveRecord::Base. Поскольку ассоциация добавляет метод с таким же именем в свою модель, использование ассоциации с тем же именем, что и имя, предоставляемое ActiveRecord::Base, переопределит метод, унаследованный через ActiveRecord::Base, и вызовет ошибки. Например, attributes и connection были бы плохим выбором для имён ассоциаций, потому что эти имена уже существуют в списке ActiveRecord::Base методов экземпляров.

Автоматически сгенерированные методы

См. также «Методы экземпляра публичного использования» ниже (из belongs_to) для получения более подробной информации.

Однозначные ассоциации (один к одному)

                                  |            |  belongs_to  |
generated methods                 | belongs_to | :polymorphic | has_one
----------------------------------+------------+--------------+---------
other                             |     X      |      X       |    X
other=(other)                     |     X      |      X       |    X
build_other(attributes={})        |     X      |              |    X
create_other(attributes={})       |     X      |              |    X
create_other!(attributes={})      |     X      |              |    X
reload_other                      |     X      |      X       |    X
other_changed?                    |     X      |      X       |
other_previously_changed?         |     X      |      X       |

Коллекционные ассоциации (один ко многим / много ко многим)

                                  |       |          | has_many
generated methods                 | habtm | has_many | :through
----------------------------------+-------+----------+----------
others                            |   X   |    X     |    X
others=(other,other,...)          |   X   |    X     |    X
other_ids                         |   X   |    X     |    X
other_ids=(id,id,...)             |   X   |    X     |    X
others<<                          |   X   |    X     |    X
others.push                       |   X   |    X     |    X
others.concat                     |   X   |    X     |    X
others.build(attributes={})       |   X   |    X     |    X
others.create(attributes={})      |   X   |    X     |    X
others.create!(attributes={})     |   X   |    X     |    X
others.size                       |   X   |    X     |    X
others.length                     |   X   |    X     |    X
others.count                      |   X   |    X     |    X
others.sum(*args)                 |   X   |    X     |    X
others.empty?                     |   X   |    X     |    X
others.clear                      |   X   |    X     |    X
others.delete(other,other,...)    |   X   |    X     |    X
others.delete_all                 |   X   |    X     |    X
others.destroy(other,other,...)   |   X   |    X     |    X
others.destroy_all                |   X   |    X     |    X
others.find(*args)                |   X   |    X     |    X
others.exists?                    |   X   |    X     |    X
others.distinct                   |   X   |    X     |    X
others.reset                      |   X   |    X     |    X
others.reload                     |   X   |    X     |    X

Переопределение сгенерированных методов

Методы ассоциаций генерируются в модуле, включённом в класс модели, что делает переопределение лёгким. Таким образом, исходный сгенерированный метод можно вызвать с помощью super:

class Car < ActiveRecord::Base
  belongs_to :owner
  belongs_to :old_owner

  def owner=(new_owner)
    self.old_owner = self.owner
    super
  end
end

Модуль методов ассоциаций включается сразу после модуля методов сгенерированных атрибутов, что означает, что ассоциация переопределит методы для атрибута с тем же именем.

Кардинальность и ассоциации

Ассоциации Active Record могут использоваться для описания отношений один-к-одному, один-ко-многим и много-ко-многим между моделями. Каждая модель использует ассоциацию для описания своей роли в отношении. Ассоциация belongs_to всегда используется в модели, которая имеет внешний ключ.

Один-к-одному

Используйте has_one в базовой модели и belongs_to в связанной модели.

class Employee < ActiveRecord::Base
  has_one :office
end
class Office < ActiveRecord::Base
  belongs_to :employee    # foreign key - employee_id
end

Один-ко-многим

Используйте has_many в базовой модели и belongs_to в связанной модели.

class Manager < ActiveRecord::Base
  has_many :employees
end
class Employee < ActiveRecord::Base
  belongs_to :manager     # foreign key - manager_id
end

Много-ко-многим

Существует два способа построения отношения много-ко-многим.

Первый способ использует ассоциацию has_many с опцией :through и моделью соединения, поэтому есть два этапа ассоциаций.

class Assignment < ActiveRecord::Base
  belongs_to :programmer  # foreign key - programmer_id
  belongs_to :project     # foreign key - project_id
end
class Programmer < ActiveRecord::Base
  has_many :assignments
  has_many :projects, through: :assignments
end
class Project < ActiveRecord::Base
  has_many :assignments
  has_many :programmers, through: :assignments
end

Для второго способа используйте has_and_belongs_to_many в обеих моделях. Это требует таблицы соединения, которая не имеет соответствующей модели или первичного ключа.

class Programmer < ActiveRecord::Base
  has_and_belongs_to_many :projects       # foreign keys in the join table
end
class Project < ActiveRecord::Base
  has_and_belongs_to_many :programmers    # foreign keys in the join table
end

Выбор способа построения отношения много-ко-многим не всегда прост. Если вам нужно работать с моделью отношения как со своей собственной сущностью, используйте has_many :through. Используйте has_and_belongs_to_many при работе со схемами устаревшей версии или когда вы никогда не работаете напрямую с самим отношением.

Это ассоциация belongs_to или has_one?

Оба выражают отношение 1-к-1. Разница в основном заключается в том, где разместить внешний ключ, который находится в таблице класса, объявляющего отношение belongs_to.

class User < ActiveRecord::Base
  # I reference an account.
  belongs_to :account
end

class Account < ActiveRecord::Base
  # One user references me.
  has_one :user
end

Таблицы для этих классов могут выглядеть примерно так:

CREATE TABLE users (
  id bigint NOT NULL auto_increment,
  account_id bigint default NULL,
  name varchar default NULL,
  PRIMARY KEY  (id)
)

CREATE TABLE accounts (
  id bigint NOT NULL auto_increment,
  name varchar default NULL,
  PRIMARY KEY  (id)
)

Несохранённые объекты и ассоциации

Вы можете манипулировать объектами и ассоциациями до их сохранения в базе данных, но при этом нужно учитывать некоторые особенности, главным образом связанные с сохранением связанных объектов.

Вы можете установить опцию :autosave для ассоциаций has_one, belongs_to, has_many или has_and_belongs_to_many. Установка её в значение true всегда сохранит члены, в то время как установка её в значение false никогда не сохранит членов. Более подробная информация об опции :autosave доступна по адресу AutosaveAssociation.

Однозначные ассоциации

  • Назначение объекта для ассоциации has_one автоматически сохраняет этот объект и замещаемый объект (если он есть), чтобы обновить их внешние ключи — за исключением случаев, когда родительский объект не сохранён (new_record? == true).

  • Если любое из этих сохранений завершится неудачей (поскольку один из объектов недействителен), генерируется исключение ActiveRecord::RecordNotSaved, и присвоение отменяется.

  • Если вы хотите назначить объект для ассоциации has_one без его сохранения, используйте метод #build_association (документирован ниже). Замещаемый объект всё равно будет сохранён для обновления его внешнего ключа.

  • Присвоение объекта для ассоциации belongs_to не сохраняет объект, поскольку поле внешнего ключа принадлежит родителю. Он также не сохраняет родителя.

Коллекции

  • Добавление объекта в коллекцию (has_many или has_and_belongs_to_many) автоматически сохраняет этот объект, за исключением случаев, когда родительский объект (владелец коллекции) ещё не хранится в базе данных.

  • Если сохранение любого из добавляемых в коллекцию объектов (через push или подобные методы) завершится неудачей, то push возвращает false.

  • Если сохранение завершится неудачей при замене коллекции (через association= ), генерируется исключение ActiveRecord::RecordNotSaved, и присвоение отменяется.

  • Вы можете добавить объект в коллекцию без автоматического сохранения, используя метод collection.build (документирован ниже).

  • Все несохранённые (new_record? == true ) члены коллекции автоматически сохраняются при сохранении родительского объекта.

Настройка запроса

Ассоциации строятся из объектов Relation, и вы можете использовать синтаксис Relation для их настройки. Например, чтобы добавить условие:

class Blog < ActiveRecord::Base
  has_many :published_posts, -> { where(published: true) }, class_name: 'Post'
end

Внутри блока -> { ... } вы можете использовать все обычные методы Relation.

Доступ к объекту владельца

Иногда полезно получить доступ к объекту владельца при построении запроса. Владелец передаётся в блок в качестве параметра. Например, следующая ассоциация найдёт все события, происходящие в день рождения пользователя:

class User < ActiveRecord::Base
  has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event'
end

Примечание: Объединение или жадное загрузка таких ассоциаций невозможны, поскольку эти операции происходят до создания экземпляра. Такие ассоциации могут быть предварительно загружены, но это выполнит N+1 запросов, так как для каждой записи будет другой диапазон (аналогично предварительной загрузке полиморфных диапазонов).

Обработчики событий ассоциаций

Аналогично обычным обработчикам событий, которые прикрепляются к жизненному циклу объекта Active Record, вы также можете определять обработчики событий, которые срабатывают при добавлении или удалении объекта из коллекции ассоциаций.

class Firm < ActiveRecord::Base
  has_many :clients,
           dependent: :destroy,
           after_add: :congratulate_client,
           after_remove: :log_after_remove

  def congratulate_client(client)
    # ...
  end

  def log_after_remove(client)
    # ...
  end
end

Callbacks могут быть определены тремя способами:

  1. Символ, который ссылается на метод, определённый в классе с связанной коллекцией. Например, after_add: :congratulate_client вызывает Firm#congratulate_client(client).

  2. Вызываемый объект со схемой, принимающей как запись с связанной коллекцией, так и запись, добавляемую или удаляемую. Например, after_add: ->(firm, client) { ... }.

  3. Объект, который отвечает на имя обработчика событий. Например, передача after_add: CallbackObject.new вызывает CallbackObject#after_add(firm, client).

Можно накладывать обработчики событий, передавая их в виде массива. Пример:

class CallbackObject
  def after_add(firm, client)
    firm.log << "after_adding #{client.id}"
  end
end

class Firm < ActiveRecord::Base
  has_many :clients,
           dependent: :destroy,
           after_add: [
             :congratulate_client,
             -> (firm, client) { firm.log << "after_adding #{client.id}" },
             CallbackObject.new
           ],
           after_remove: :log_after_remove
end

Возможные обработчики событий: before_add, after_add, before_remove, и after_remove.

Если любой из обработчиков событий before_add вызывает исключение, объект не будет добавлен в коллекцию.

Аналогично, если любой из обработчиков событий before_remove вызывает исключение, объект не будет удалён из коллекции.

Примечание: Для срабатывания обработчиков удаления необходимо использовать методы destroy / destroy_all. Например:

  • firm.clients.destroy(client)

  • firm.clients.destroy(*clients)

  • firm.clients.destroy_all

Методы delete / delete_all , такие как следующие, не вызывают обработчики удаления:

  • firm.clients.delete(client)

  • firm.clients.delete(*clients)

  • firm.clients.delete_all

Расширения ассоциаций

Объекты-прокси, контролирующие доступ к ассоциациям, могут быть расширены с помощью анонимных модулей. Это особенно полезно для добавления новых методов поиска, создания и других методов типа фабрики, которые используются только в рамках данной ассоциации.

class Account < ActiveRecord::Base
  has_many :people do
    def find_or_create_by_name(name)
      first_name, last_name = name.split(" ", 2)
      find_or_create_by(first_name: first_name, last_name: last_name)
    end
  end
end

person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name  # => "Heinemeier Hansson"

Если вам нужно использовать одни и те же расширения для многих ассоциаций, вы можете использовать именованный модуль расширений.

module FindOrCreateByNameExtension
  def find_or_create_by_name(name)
    first_name, last_name = name.split(" ", 2)
    find_or_create_by(first_name: first_name, last_name: last_name)
  end
end

class Account < ActiveRecord::Base
  has_many :people, -> { extending FindOrCreateByNameExtension }
end

class Company < ActiveRecord::Base
  has_many :people, -> { extending FindOrCreateByNameExtension }
end

Некоторые расширения могут работать только с пониманием внутренних аспектов ассоциации. Расширения могут получить доступ к соответствующему состоянию, используя следующие методы (где items — имя ассоциации):

  • record.association(:items).owner — Возвращает объект, к которому относится ассоциация.

  • record.association(:items).reflection — Возвращает объект рефлексии, описывающий ассоциацию.

  • record.association(:items).target — Возвращает связанный объект для belongs_to и has_one, или коллекцию связанных объектов для has_many и has_and_belongs_to_many.

Однако, внутри собственно кода расширения вы не будете иметь доступа к record как выше. В этом случае вы можете получить доступ к proxy_association. Например, record.association(:items) и record.items.proxy_association вернут один и тот же объект, позволяя вам делать вызовы, такие как proxy_association.owner внутри расширений ассоциаций.

Модели соединения ассоциаций

Ассоциации «многие ко многим» могут быть настроены с помощью параметра :through для использования явной модели соединения для извлечения данных. Это работает аналогично ассоциации has_and_belongs_to_many. Преимущество заключается в том, что вы можете добавить валидации, обратные вызовы и дополнительные атрибуты в модели соединения. Рассмотрим следующую схему:

class Author < ActiveRecord::Base
  has_many :authorships
  has_many :books, through: :authorships
end

class Authorship < ActiveRecord::Base
  belongs_to :author
  belongs_to :book
end

@author = Author.first
@author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
@author.books                              # selects all books by using the Authorship join model

Вы также можете использовать ассоциацию has_many в модели соединения:

class Firm < ActiveRecord::Base
  has_many   :clients
  has_many   :invoices, through: :clients
end

class Client < ActiveRecord::Base
  belongs_to :firm
  has_many   :invoices
end

class Invoice < ActiveRecord::Base
  belongs_to :client
end

@firm = Firm.first
@firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
@firm.invoices                            # selects all invoices by going through the Client join model

Аналогично, вы можете использовать ассоциацию has_one в модели соединения:

class Group < ActiveRecord::Base
  has_many   :users
  has_many   :avatars, through: :users
end

class User < ActiveRecord::Base
  belongs_to :group
  has_one    :avatar
end

class Avatar < ActiveRecord::Base
  belongs_to :user
end

@group = Group.first
@group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
@group.avatars                                # selects all avatars by going through the User join model.

Важно помнить, что использование ассоциаций has_one или has_many в модели соединения ограничено чтением. Например, следующее не будет работать после предыдущего примера:

@group.avatars << Avatar.new   # this would work if User belonged_to Avatar rather than the other way around
@group.avatars.delete(@group.avatars.last)  # so would this

Настройка обратных ссылок

Если вы используете belongs_to в модели соединения, рекомендуется установить параметр :inverse_of для belongs_to. Это означает, что следующий пример будет работать правильно (где tags — ассоциация has_many :through):

@post = Post.first
@tag = @post.tags.build name: "ruby"
@tag.save

Последняя строка должна сохранить запись через посредника (Tagging). Это будет работать только если установлен параметр :inverse_of:

class Tagging < ActiveRecord::Base
  belongs_to :post
  belongs_to :tag, inverse_of: :taggings
end

Если вы не устанавливаете запись :inverse_of, ассоциация постарается сопоставить себя с правильной обратной ссылкой. Автоматическое определение обратной ссылки работает только для ассоциаций has_many, has_one и belongs_to.

Параметры :foreign_key и :through в ассоциациях также могут препятствовать автоматическому поиску обратной ссылки, как и пользовательские области в некоторых случаях. Более подробная информация в руководстве по ассоциациям Active Record .

Автоматическое определение обратной ссылки использует эвристику, основанную на имени класса, поэтому она может не работать для всех ассоциаций, особенно для тех, у которых нестандартные имена.

Вы можете отключить автоматическое определение обратной ссылки, установив параметр :inverse_of в значение false следующим образом:

class Tagging < ActiveRecord::Base
  belongs_to :tag, inverse_of: false
end

Вложенные ассоциации

Вы можете указать любую ассоциацию с параметром :through, включая ассоциацию, которая имеет параметр :through.

class Author < ActiveRecord::Base
  has_many :posts
  has_many :comments, through: :posts
  has_many :commenters, through: :comments
end

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :commenter
end

@author = Author.first
@author.commenters # => People who commented on posts written by the author

Эквивалентный способ настройки этой ассоциации:

class Author < ActiveRecord::Base
  has_many :posts
  has_many :commenters, through: :posts
end

class Post < ActiveRecord::Base
  has_many :comments
  has_many :commenters, through: :comments
end

class Comment < ActiveRecord::Base
  belongs_to :commenter
end

При использовании вложенной ассоциации вы не сможете её изменить, так как нет достаточной информации для понимания изменения. Например, если вы попытаетесь добавить Commenter в примере выше, не будет способа установить промежуточные объекты Post и Comment.

Полиморфные ассоциации

Полиморфные ассоциации моделей не ограничены типами моделей, с которыми они могут быть связаны. Вместо этого они задают интерфейс, которому должна соответствовать ассоциация has_many.

class Asset < ActiveRecord::Base
  belongs_to :attachable, polymorphic: true
end

class Post < ActiveRecord::Base
  has_many :assets, as: :attachable         # The :as option specifies the polymorphic interface to use.
end

@asset.attachable = @post

Это работает с использованием столбца типа в дополнение к внешнему ключу для указания связанной записи. В примере с активами вам потребуется целое число attachable_id и строковый столбец attachable_type.

Использование полиморфных ассоциаций в сочетании с наследованием одной таблицы (STI) немного сложно. Чтобы ассоциации работали как ожидается, убедитесь, что вы сохраняете базовую модель для моделей STI в столбце типа полиморфной ассоциации. Продолжая пример с активами, предположим, что гостевые и пользовательские публикации используют таблицу публикаций для STI. В этом случае в таблице публикаций должен быть столбец type.

Примечание: Метод attachable_type= вызывается при присвоении attachable. Тип attachable передаётся в виде String.

class Asset < ActiveRecord::Base
  belongs_to :attachable, polymorphic: true

  def attachable_type=(class_name)
     super(class_name.constantize.base_class.to_s)
  end
end

class Post < ActiveRecord::Base
  # because we store "Post" in attachable_type now dependent: :destroy will work
  has_many :assets, as: :attachable, dependent: :destroy
end

class GuestPost < Post
end

class MemberPost < Post
end

Кэширование

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

project.milestones             # fetches milestones from the database
project.milestones.size        # uses the milestone cache
project.milestones.empty?      # uses the milestone cache
project.milestones.reload.size # fetches milestones from the database
project.milestones             # uses the milestone cache

Леничная загрузка ассоциаций

Леничная загрузка — это способ поиска объектов определённого класса и нескольких именованных ассоциаций. Это один из самых простых способов предотвратить проблему N+1, в которой извлечение 100 публикаций, для отображения каждой из которых требуется автор, приводит к 101 базе данных запросов. С помощью леничной загрузки количество запросов уменьшится с 101 до 2.

class Post < ActiveRecord::Base
  belongs_to :author
  has_many   :comments
end

Рассмотрим следующий цикл, используя класс выше:

Post.all.each do |post|
  puts "Post:            " + post.title
  puts "Written by:      " + post.author.name
  puts "Last comment on: " + post.comments.first.created_on
end

Для перебора этих ста публикаций мы получим 201 запрос к базе данных. Давайте сначала оптимизируем его для извлечения автора:

Post.includes(:author).each do |post|

Это ссылается на имя ассоциации belongs_to, которая также использовала символ :author. После загрузки публикаций find соберет author_id из каждой и загрузит все упомянутых авторов одним запросом. Это уменьшит количество запросов с 201 до 102.

Мы можем дополнительно улучшить ситуацию, указав обе ассоциации в методе поиска:

Post.includes(:author, :comments).each do |post|

Это загрузит все комментарии одним запросом. Это уменьшает общее количество запросов до 3. В целом, количество запросов будет равно 1 плюс количество указанных ассоциаций (за исключением случаев, когда некоторые ассоциации являются полиморфными belongs_to — см. ниже).

Для включения глубокой иерархии ассоциаций используйте хэш:

Post.includes(:author, { comments: { author: :gravatar } }).each do |post|

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

Всё это могущество не должно вводить вас в заблуждение, что вы можете извлекать огромные объёмы данных без штрафа по производительности только потому, что вы уменьшили количество запросов. Базе данных всё равно нужно отправлять все данные в Active Record, и всё равно их нужно обрабатывать. Поэтому это не панацея от проблем производительности, но это отличный способ сократить количество запросов в ситуации, как описанной выше.

Поскольку загружается только одна таблица за раз, условия или порядок не могут ссылаться на таблицы, отличные от основной. В этом случае Active Record возвращается к ранее используемой стратегии LEFT OUTER JOIN. Например:

Post.includes([:author, :comments]).where(['comments.approved = ?', true])

Это приведёт к одному SQL-запросу со соединениями, примерно так: LEFT OUTER JOIN comments ON comments.post_id = posts.id и LEFT OUTER JOIN authors ON authors.id = posts.author_id. Обратите внимание, что использование таких условий может иметь непредвиденные последствия. В приведённом примере публикации без одобренных комментариев вообще не возвращаются, так как условия применяются к всему SQL-запросу, а не только к ассоциации.

Для разграничения ссылок на столбцы необходимо, чтобы произошла эта отмена, например, order: "author.name DESC" будет работать, но order: "name DESC" нет.

Если вы хотите загрузить все публикации (включая публикации без одобренных комментариев), напишите свой собственный запрос LEFT OUTER JOIN с использованием ON:

Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")

В этом случае обычно естественнее включить ассоциацию, на которой определены условия:

class Post < ActiveRecord::Base
  has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment'
end

Post.includes(:approved_comments)

Это загрузит публикации и ленично загрузит ассоциацию approved_comments, которая содержит только одобренные комментарии.

Если вы ленично загружаете ассоциацию со специфическим параметром :limit , он будет проигнорирован, вернув все связанные объекты:

class Picture < ActiveRecord::Base
  has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
end

Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.

Леничная загрузка поддерживается с полиморфными ассоциациями.

class Address < ActiveRecord::Base
  belongs_to :addressable, polymorphic: true
end

Вызов, который пытается ленично загрузить модель addressable

Address.includes(:addressable)

Это выполнит один запрос для загрузки адресов и загрузит адресабельныe элементы с одним запросом на каждый тип адресабельного элемента. Например, если все адресабельные элементы относятся к классу Person или Company, то будет выполнено всего 3 запроса. Список типов адресабельных элементов для загрузки определяется на основе загруженных адресов. Это не поддерживается, если Active Record должен вернуться к предыдущей реализации ленивой загрузки и вызовет ActiveRecord::EagerLoadPolymorphicError. Причина в том, что тип родительской модели является значением столбца, поэтому имя соответствующей таблицы нельзя поместить в FROM/JOIN указание этого запроса.

Имена таблиц

Active Record использует алиасы таблиц в случае, если к таблице обращаются несколько раз в соединении. Если к таблице обращаются только один раз, используется стандартное имя таблицы. Во второй раз таблица алиасируется как #{reflection_name}_#{parent_table_name}. Индексы добавляются для любых последующих обращений к имени таблицы.

Post.joins(:comments)
# SELECT ... FROM posts INNER JOIN comments ON ...
Post.joins(:special_comments) # STI
# SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
# SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts

Пример дерева:

TreeMixin.joins(:children)
# SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
TreeMixin.joins(children: :parent)
# SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
#                        INNER JOIN parents_mixins ...
TreeMixin.joins(children: {parent: :children})
# SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
#                        INNER JOIN parents_mixins ...
#                        INNER JOIN mixins childrens_mixins_2

Таблицы соединения «многие-ко-многим» используют ту же идею, но добавляют суффикс _join:

Post.joins(:categories)
# SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
Post.joins(categories: :posts)
# SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
#                       INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
Post.joins(categories: {posts: :categories})
# SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
#                       INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
#                       INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2

Если вы хотите указать собственные пользовательские соединения с помощью метода ActiveRecord::QueryMethods#joins, эти имена таблиц будут иметь приоритет над ленивыми ассоциациями:

Post.joins(:comments).joins("inner join comments ...")
# SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
Post.joins(:comments, :special_comments).joins("inner join comments ...")
# SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
#                       INNER JOIN comments special_comments_posts ...
#                       INNER JOIN comments ...

Имена алиасов таблиц автоматически усекаются в соответствии с максимальной длиной идентификаторов таблиц в зависимости от конкретной базы данных.

Модули

По умолчанию ассоциации будут искать объекты в рамках текущего пространства имен модуля. Рассмотрим:

module MyApplication
  module Business
    class Firm < ActiveRecord::Base
      has_many :clients
    end

    class Client < ActiveRecord::Base; end
  end
end

Когда вызывается Firm#clients, она в свою очередь вызовет MyApplication::Business::Client.find_all_by_firm_id(firm.id). Если вы хотите связать класс из другого пространства имен модуля, можно указать полное имя класса.

module MyApplication
  module Business
    class Firm < ActiveRecord::Base; end
  end

  module Billing
    class Account < ActiveRecord::Base
      belongs_to :firm, class_name: "MyApplication::Business::Firm"
    end
  end
end

Взаимные ассоциации

Когда вы указываете ассоциацию, обычно существует ассоциация в модели, которая описывает те же отношения в обратном направлении. Например, с помощью следующих моделей:

class Dungeon < ActiveRecord::Base
  has_many :traps
  has_one :evil_wizard
end

class Trap < ActiveRecord::Base
  belongs_to :dungeon
end

class EvilWizard < ActiveRecord::Base
  belongs_to :dungeon
end

Ассоциация traps в модели Dungeon и ассоциация dungeon в модели Trap являются обратными друг другу, а обратной ассоциации dungeon в модели EvilWizard является ассоциация evil_wizard в модели Dungeon (и наоборот). По умолчанию Active Record может определить обратную ассоциацию на основе имени класса. Результат будет следующим:

d = Dungeon.first
t = d.traps.first
d.object_id == t.dungeon.object_id # => true

Экземпляры Dungeon d и t.dungeon в приведенном выше примере ссылаются на один и тот же экземпляр в памяти, так как ассоциация соответствует имени класса. Результат будет таким же, если мы добавим :inverse_of в определения наших моделей:

class Dungeon < ActiveRecord::Base
  has_many :traps, inverse_of: :dungeon
  has_one :evil_wizard, inverse_of: :dungeon
end

class Trap < ActiveRecord::Base
  belongs_to :dungeon, inverse_of: :traps
end

class EvilWizard < ActiveRecord::Base
  belongs_to :dungeon, inverse_of: :evil_wizard
end

Для получения дополнительной информации см. документацию по параметру :inverse_of и руководство по ассоциациям Active Record Active Record Associations guide.

Удаление из ассоциаций

Зависимые ассоциации

has_many, has_one и belongs_to ассоциации поддерживают параметр :dependent. Это позволяет указать, что связанные записи должны быть удалены при удалении владельца.

Например:

class Author
  has_many :posts, dependent: :destroy
end
Author.find(1).destroy # => Will destroy all of the author's posts, too

Параметр :dependent может иметь разные значения, которые указывают, как выполняется удаление. Для получения дополнительной информации см. документацию по этому параметру для разных типов ассоциаций. Если параметр не указан, поведение заключается в том, что со связанными записями ничего не происходит при уничтожении записи.

Обратите внимание, что :dependent реализуется с помощью системы обратного вызова Rails, которая работает путем обработки обратных вызовов в порядке очереди. Следовательно, другие объявленные до или после обратного вызова :dependent могут повлиять на его работу.

Обратите внимание, что параметр :dependent игнорируется для has_one :through ассоциаций.

Удалить или уничтожить?

has_many и has_and_belongs_to_many ассоциации имеют методы destroy, delete, destroy_all и delete_all.

Для has_and_belongs_to_many, delete и destroy одинаковы: они удаляют записи в таблице соединения.

Для has_many, destroy и destroy_all всегда вызывают метод destroy удаляемой записи(ей), чтобы запустить обратные вызовы. Однако delete и delete_all либо выполнят удаление в соответствии со стратегией, указанной параметром :dependent, либо, если параметр :dependent не указан, то будет применена стратегия по умолчанию. Стратегия по умолчанию ничего не делает (оставляет внешние ключи с родительскими идентификаторами), за исключением has_many :through, где стратегия по умолчанию — delete_all (удалить записи соединения без выполнения их обратных вызовов).

Также есть метод clear, который идентичен методу delete_all, за исключением того, что он возвращает ассоциацию, а не записи, которые были удалены.

Что удаляется?

Здесь есть потенциальная ловушка: has_and_belongs_to_many и has_many :through ассоциации имеют записи в таблицах соединения, а также связанные записи. Итак, когда мы вызываем один из этих методов удаления, что именно должно быть удалено?

Ответ заключается в том, что предполагается, что удаление по ассоциации связано с удалением связи между владельцем и связанным объектом(ами), а не обязательно самими связанными объектами. Итак, с has_and_belongs_to_many и has_many :through, записи в таблице соединения будут удалены, но сами связанные записи нет.

Это имеет смысл, если подумать об этом: если вы вызовете post.tags.delete(Tag.find_by(name: 'food')), вы захотите отсоединить тег «еда» от записи, а не удалить сам тег из базы данных.

Однако есть примеры, где эта стратегия не имеет смысла. Например, предположим, что у человека есть много проектов, и каждый проект имеет много задач. Если мы удалим одну из задач человека, мы, вероятно, не захотим удалять проект. В этом случае метод удаления фактически не сработает: он может использоваться только в том случае, если ассоциация в модели соединения является belongs_to. В других ситуациях ожидается выполнение операций непосредственно со связанными записями или с ассоциацией :through.

С обычной has_many нет различий между «связанными записями» и «связью», поэтому существует только один выбор того, что удаляется.

С has_and_belongs_to_many и has_many :through, если вы хотите удалить сами связанные записи, вы всегда можете сделать что-то вроде person.tasks.each(&:destroy).

Безопасность типов с ActiveRecord::AssociationTypeMismatch

Если вы попытаетесь назначить объект ассоциации, который не соответствует выведенному или указанному :class_name, вы получите ActiveRecord::AssociationTypeMismatch.

Параметры

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

Публичные методы экземпляров

belongs_to(name, scope = nil, **options) Показать исходный код
# File activerecord/lib/active_record/associations.rb, line 1689
def belongs_to(name, scope = nil, **options)
  reflection = Builder::BelongsTo.build(self, name, scope, options)
  Reflection.add_reflection self, name, reflection
end

Указывает однозначное соответствие с другим классом. Этот метод следует использовать только в том случае, если этот класс содержит внешний ключ. Если внешний ключ содержится в другом классе, то следует использовать has_one вместо него. См. Is it a belongs_to or has_one association? для получения более подробной информации о том, когда использовать has_one, а когда использовать belongs_to.

Будут добавлены методы для извлечения и запроса одного связанного объекта, для которого этот объект хранит идентификатор:

association является заглушкой для символа, переданного в качестве аргумента name, поэтому belongs_to :author добавит, среди прочего, author.nil?.

association

Возвращает связанный объект. nil возвращается, если он не найден.

association=(associate)

Присваивает связанный объект, извлекает первичный ключ и устанавливает его как внешний ключ. Никаких изменений или удалений существующих записей не происходит.

build_association(attributes = {})

Возвращает новый объект связанного типа, который был создан с attributes и связан с этим объектом через внешний ключ, но еще не сохранен.

create_association(attributes = {})

Возвращает новый объект связанного типа, который был создан с attributes, связан с этим объектом через внешний ключ и уже сохранен (если он прошел валидацию).

create_association!(attributes = {})

Делает то же, что и create_association, но вызывает ActiveRecord::RecordInvalid, если запись некорректна.

reload_association

Возвращает связанный объект, принудительно выполняя чтение из базы данных.

reset_association

Выгружает связанный объект. Следующий доступ запросит его из базы данных.

association_changed?

Возвращает true, если новый связанный объект был назначен, и при следующем сохранении будет обновлен внешний ключ.

association_previously_changed?

Возвращает true, если предыдущее сохранение обновило ассоциацию для ссылки на новый связанный объект.

Пример

class Post < ActiveRecord::Base
  belongs_to :author
end

Объявление belongs_to :author добавляет следующие методы (и другие):

post = Post.find(7)
author = Author.find(19)

post.author           # similar to Author.find(post.author_id)
post.author = author  # similar to post.author_id = author.id
post.build_author     # similar to post.author = Author.new
post.create_author    # similar to post.author = Author.new; post.author.save; post.author
post.create_author!   # similar to post.author = Author.new; post.author.save!; post.author
post.reload_author
post.reset_author
post.author_changed?
post.author_previously_changed?

Скопы

Вы можете передать второй аргумент scope как вызываемый объект (например, proc или lambda) для извлечения конкретной записи или настройки сгенерированного запроса при доступе к связанному объекту.

Примеры скопов:

belongs_to :firm, -> { where(id: 2) }
belongs_to :user, -> { joins(:friends) }
belongs_to :level, ->(game) { where("game_level > ?", game.current_level) }

Параметры

Объявление также может содержать options хэш для специализации поведения ассоциации.

:class_name

Укажите имя класса ассоциации. Используйте его только в том случае, если это имя нельзя определить по имени ассоциации. Так belongs_to :author по умолчанию будет связан с классом Author, но если фактическое имя класса — Person, вам нужно будет указать его с помощью этого параметра.

:foreign_key

Укажите внешний ключ, используемый для ассоциации. По умолчанию он определяется по имени ассоциации с суффиксом «_id». Таким образом, класс, который определяет ассоциацию belongs_to :person, будет использовать «person_id» в качестве значения по умолчанию :foreign_key. Аналогично, belongs_to :favorite_person, class_name: "Person" будет использовать внешний ключ «favorite_person_id».

Установка параметра :foreign_key предотвращает автоматическое обнаружение обратной ассоциации, поэтому обычно рекомендуется также установить параметр :inverse_of.

:foreign_type

Укажите столбец, используемый для хранения типа связанного объекта, если это полиморфная ассоциация. По умолчанию он определяется по имени ассоциации с суффиксом «_type». Таким образом, класс, который определяет ассоциацию belongs_to :taggable, polymorphic: true, будет использовать «taggable_type» в качестве значения по умолчанию :foreign_type.

:primary_key

Укажите метод, возвращающий первичный ключ связанного объекта, используемого для ассоциации. По умолчанию это id.

:dependent

Если установлено значение :destroy, связанный объект будет уничтожен при уничтожении этого объекта. Если установлено значение :delete, связанный объект будет удален без вызова его метода destroy. Если установлено значение :destroy_async, связанный объект запланирован на уничтожение в фоновом задании. Этот параметр не следует указывать, когда belongs_to используется в сочетании с отношением has_many в другом классе из-за возможности оставить сироты.

:counter_cache

Кэширует количество принадлежащих объектов в классе-партнере с помощью CounterCache::ClassMethods#increment_counter и CounterCache::ClassMethods#decrement_counter. Кэш счетчика увеличивается при создании объекта этого класса и уменьшается при его уничтожении. Это требует, чтобы в классе-партнере (например, в классе Post) использовался столбец с именем #{table_name}_count (например, comments_count для класса Comment) — то есть миграция для #{table_name}_count создается в классе-партнере (так что Post.comments_count вернет сохраненный счет). Вы также можете указать пользовательский столбец счетчика, указав имя столбца вместо значения true/false этого параметра (например, counter_cache: :my_custom_counter).

Начало использования счетчиков на существующих больших таблицах может быть проблематичным, так как значения столбцов должны быть заполнены отдельно от добавления столбца (чтобы не блокировать таблицу слишком долго) и перед использованием :counter_cache (иначе методы, такие как size/any? и т. д., которые используют счетчики в качестве счетчиков, могут давать некорректные результаты). Чтобы безопасно заполнить значения, поддерживая обновление столбцов счетчиков при создании/удалении записей дочерних элементов и избегая упомянутых методов, используйте counter_cache: { active: false }. Если вам также нужно указать пользовательское имя столбца, используйте counter_cache: { active: false, column: :my_custom_counter }.

Примечание: Если вы включили счетчик, вам может понадобиться добавить атрибут счетчика в список attr_readonly в связанных классах (например, class Post; attr_readonly :comments_count; end).

:polymorphic

Укажите, что эта ассоциация является полиморфной, передав true. Примечание: поскольку полиморфные ассоциации полагаются на хранение имен классов в базе данных, убедитесь, что вы обновили имена классов в столбце полиморфного типа *_type соответствующих строк.

:validate

Если установлено значение true, валидирует новые объекты, добавленные к ассоциации, при сохранении родительского объекта. false по умолчанию. Если вы хотите гарантировать, что связанные объекты перевалидируются при каждом обновлении, используйте validates_associated.

:autosave

Если true, всегда сохраняет или уничтожает связанный объект, если он помечен для уничтожения, при сохранении родительского объекта. Если false, никогда не сохраняет и не уничтожает связанный объект. По умолчанию сохраняет только связанный объект, если это новая запись.

Обратите внимание, что NestedAttributes::ClassMethods#accepts_nested_attributes_for устанавливает :autosave в true.

:touch

Если true, связанный объект будет отмечен (атрибуты updated_at / updated_on установлены на текущее время) при сохранении или уничтожении этой записи. Если вы укажете символ, этот атрибут будет обновлен текущим временем, а также атрибуты updated_at / updated_on. Обратите внимание, что никакой проверки не будет выполнено примечании, и будут выполнены только колбеки after_touch, after_commit, и after_rollback.

:inverse_of

Указывает имя has_one или has_many ассоциации связанного объекта, которая является обратной ассоциацией belongs_to. Подробнее см. Bi-directional associations.

:optional

При установке в true, валидация присутствия ассоциации не будет выполняться.

:required

При установке в true, валидация присутствия ассоциации также будет выполняться. Это проверит саму ассоциацию, а не ID. Вы можете использовать :inverse_of для предотвращения дополнительного запроса во время валидации. ПРИМЕЧАНИЕ: required установлено в true по умолчанию и устарело. Если вы не хотите выполнять валидацию присутствия ассоциации, используйте optional: true.

:default

Предоставьте вызываемый объект (т. е. proc или lambda) для указания того, что ассоциация должна быть инициализирована конкретной записью перед проверкой. Обратите внимание, что вызываемый объект не будет выполнен, если запись существует.

:strict_loading

Принудительно загружает каждый раз, когда связанная запись загружается через эту ассоциацию.

:ensuring_owner_was

Указывает метод экземпляра, который вызывается у владельца. Метод должен возвращать true, чтобы связанные записи могли быть удалены в фоновом задании.

:query_constraints

Выступает в качестве составного внешнего ключа. Определяет список столбцов, которые будут использоваться для запроса связанного объекта. Это необязательный параметр. По умолчанию Rails попытается получить значение автоматически. Если значение установлено, размер Array должен соответствовать первичному ключу связанной модели или размеру query_constraints.

Примеры параметров:

belongs_to :firm, foreign_key: "client_of"
belongs_to :person, primary_key: "name", foreign_key: "person_name"
belongs_to :author, class_name: "Person", foreign_key: "author_id"
belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
                          class_name: "Coupon", foreign_key: "coupon_id"
belongs_to :attachable, polymorphic: true
belongs_to :project, -> { readonly }
belongs_to :post, counter_cache: true
belongs_to :comment, touch: true
belongs_to :company, touch: :employees_last_updated_at
belongs_to :user, optional: true
belongs_to :account, default: -> { company.account }
belongs_to :account, strict_loading: true
belongs_to :note, query_constraints: [:organization_id, :note_id]
has_and_belongs_to_many(name, scope = nil, **options, &extension) Показать исходный код
# File activerecord/lib/active_record/associations.rb, line 1870
        def has_and_belongs_to_many(name, scope = nil, **options, &extension)
          habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)

          builder = Builder::HasAndBelongsToMany.new name, self, options

          join_model = builder.through_model

          const_set join_model.name, join_model
          private_constant join_model.name

          middle_reflection = builder.middle_reflection join_model

          Builder::HasMany.define_callbacks self, middle_reflection
          Reflection.add_reflection self, middle_reflection.name, middle_reflection
          middle_reflection.parent_reflection = habtm_reflection

          include Module.new {
            class_eval <<-RUBY, __FILE__, __LINE__ + 1
              def destroy_associations
                association(:#{middle_reflection.name}).delete_all(:delete_all)
                association(:#{name}).reset
                super
              end
            RUBY
          }

          hm_options = {}
          hm_options[:through] = middle_reflection.name
          hm_options[:source] = join_model.right_reflection.name

          [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
            hm_options[k] = options[k] if options.key? k
          end

          has_many name, scope, **hm_options, &extension
          _reflections[name].parent_reflection = habtm_reflection
        end

Определяет отношение «многие ко многим» с другим классом. Это связывает два класса через промежуточную таблицу соединения. Если таблица соединения не указана явно в качестве опции, она подбирается по лексикографическому порядку имён классов. Таким образом, соединение между Developer и Project даст имя таблицы соединения «developers_projects», поскольку «D» предшествует «P» в алфавитном порядке. Обратите внимание, что этот порядок рассчитывается с помощью оператора < для String. Это означает, что если строки имеют разную длину, и строки равны при сравнении до минимальной длины, то более длинная строка считается имеющей более высокий лексикографический приоритет, чем более короткая. Например, ожидалось бы, что таблицы «paper_boxes» и «papers» сгенерируют имя таблицы соединения «papers_paper_boxes» из-за длины имени «paper_boxes», но фактически генерируется имя таблицы соединения «paper_boxes_papers». Имейте в виду это замечание и используйте опцию :join_table если необходимо. Если ваши таблицы имеют общий префикс, он будет появляться только один раз в начале. Например, таблицы «catalog_categories» и «catalog_products» генерируют имя таблицы соединения «catalog_categories_products».

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

class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[8.0]
  def change
    create_join_table :developers, :projects
  end
end

Также рекомендуется добавить индексы к каждому из этих столбцов для ускорения процесса объединения. Однако в MySQL рекомендуется добавить составной индекс для обоих столбцов, так как MySQL использует только один индекс на таблицу во время поиска.

Добавляет следующие методы для извлечения и запроса:

collection — это плейсхолдер для символа, переданного в качестве аргумента name, поэтому has_and_belongs_to_many :categories добавит, среди прочего, categories.empty?.

collection

Возвращает Relation всех связанных объектов. Если не найдено ни одного, возвращается пустой Relation.

collection<<(object, ...)

Добавляет один или несколько объектов в коллекцию, создавая ассоциации в таблице соединения (collection.push и collection.concat — псевдонимы для этого метода). Обратите внимание, что эта операция мгновенно выполняет запрос обновления SQL без ожидания вызова сохранения или обновления родительского объекта, если родительский объект — новый запис.

collection.delete(object, ...)

Удаляет один или несколько объектов из коллекции, удаляя их ассоциации из таблицы соединения. Это не приводит к уничтожению объектов.

collection.destroy(object, ...)

Удаляет один или несколько объектов из коллекции, выполняя уничтожение каждой ассоциации в таблице соединения, переопределяя любую опцию зависимостей. Это не приводит к уничтожению объектов.

collection=objects

Заменяет содержимое коллекции, удаляя и добавляя объекты по мере необходимости.

collection_singular_ids

Возвращает массив идентификаторов связанных объектов.

collection_singular_ids=ids

Заменяет коллекцию объектами, идентифицируемыми первичными ключами в ids.

collection.clear

Удаляет все объекты из коллекции. Это не приводит к уничтожению объектов.

collection.empty?

Возвращает true, если связанных объектов нет.

collection.size

Возвращает количество связанных объектов.

collection.find(id)

Ищет связанный объект, отвечающий id и удовлетворяющий условию, что он должен быть связан с этим объектом. Использует те же правила, что и ActiveRecord::FinderMethods#find.

collection.exists?(...)

Проверяет, существует ли связанный объект с заданными условиями. Использует те же правила, что и ActiveRecord::FinderMethods#exists?.

collection.build(attributes = {})

Возвращает новый объект типа коллекции, который был создан с attributes и связан с этим объектом через таблицу соединения, но ещё не сохранён.

collection.create(attributes = {})

Возвращает новый объект типа коллекции, который был создан с attributes, связан с этим объектом через таблицу соединения и уже сохранён (если прошёл валидацию).

collection.reload

Возвращает Relation всех связанных объектов, принудительно выполняя чтение из базы данных. Если не найдено ни одного, возвращается пустой Relation.

Пример

class Developer < ActiveRecord::Base
  has_and_belongs_to_many :projects
end

Объявление has_and_belongs_to_many :projects добавляет следующие методы (и другие):

developer = Developer.find(11)
project   = Project.find(9)

developer.projects
developer.projects << project
developer.projects.delete(project)
developer.projects.destroy(project)
developer.projects = [project]
developer.project_ids
developer.project_ids = [9]
developer.projects.clear
developer.projects.empty?
developer.projects.size
developer.projects.find(9)
developer.projects.exists?(9)
developer.projects.build  # similar to Project.new(developer_id: 11)
developer.projects.create # similar to Project.create(developer_id: 11)
developer.projects.reload

Объявление может включать хеш options для специализации поведения ассоциации.

Области видимости

Вы можете передать второй аргумент scope как вызываемый объект (т. е. proc или lambda) для получения определённого набора записей или для настройки генерируемого запроса при обращении к связанной коллекции.

Примеры областей видимости:

has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
has_and_belongs_to_many :categories, ->(post) {
  where("default_category = ?", post.default_category)
}

Расширения

Аргумент extension позволяет передать блок в ассоциацию has_and_belongs_to_many. Это полезно для добавления новых методов поиска, создания и других методов типа фабрики, которые будут использоваться в качестве части ассоциации.

Примеры расширений:

has_and_belongs_to_many :contractors do
  def find_or_create_by_name(name)
    first_name, last_name = name.split(" ", 2)
    find_or_create_by(first_name: first_name, last_name: last_name)
  end
end

Параметры

:class_name

Указывает имя класса ассоциации. Используйте его только в том случае, если это имя нельзя вывести из имени ассоциации. Таким образом, has_and_belongs_to_many :projects по умолчанию будет связан с классом Project, но если реальное имя класса — SuperProject, вам нужно будет указать его с помощью этого параметра.

:join_table

Указывает имя таблицы соединения, если значение по умолчанию, основанное на лексикографическом порядке, не подходит. ПРЕДУПРЕЖДЕНИЕ: Если вы перезаписываете имя таблицы одного из классов, метод table_name ДОЛЖЕН быть объявлен под любым объявлением has_and_belongs_to_many для работы.

:foreign_key

Указывает внешний ключ, используемый для ассоциации. По умолчанию предполагается, что это имя этого класса в нижнем регистре с приставкой «_id». Таким образом, класс Person, создающий ассоциацию has_and_belongs_to_many с классом Project, будет использовать «person_id» в качестве значения по умолчанию :foreign_key.

Установка параметра :foreign_key предотвращает автоматическое обнаружение обратной ассоциации, поэтому обычно рекомендуется установить также параметр :inverse_of.

:association_foreign_key

Указывает внешний ключ, используемый для ассоциации на стороне приёма ассоциации. По умолчанию предполагается, что это имя связанного класса в нижнем регистре с приставкой «_id». Например, если класс Person создаёт ассоциацию has_and_belongs_to_many с классом Project, ассоциация будет использовать «project_id» в качестве значения по умолчанию :association_foreign_key.

:validate

При установке в true, валидирует новые объекты, добавляемые к ассоциации, при сохранении родительского объекта. true по умолчанию. Если вы хотите гарантировать, что связанные объекты перевалидируются при каждом обновлении, используйте validates_associated.

:autosave

Если значение истинно, всегда сохраняет связанные объекты или уничтожает их, если они помечены для уничтожения, при сохранении родительского объекта. Если ложно, никогда не сохраняет или не уничтожает связанные объекты. По умолчанию сохраняются только новые связанные объекты.

Обратите внимание, что NestedAttributes::ClassMethods#accepts_nested_attributes_for устанавливает :autosave в true.

:strict_loading

Принудительно загружает каждый раз, когда связанная запись загружается через эту ассоциацию.

Примеры параметров:

has_and_belongs_to_many :projects
has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
has_and_belongs_to_many :nations, class_name: "Country"
has_and_belongs_to_many :categories, join_table: "prods_cats"
has_and_belongs_to_many :categories, -> { readonly }
has_and_belongs_to_many :categories, strict_loading: true
has_many(name, scope = nil, **options, &extension) Показать исходный код
# File activerecord/lib/active_record/associations.rb, line 1302
def has_many(name, scope = nil, **options, &extension)
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
  Reflection.add_reflection self, name, reflection
end

Определяет ассоциацию один-ко-многим. Будут добавлены следующие методы для получения и запроса коллекций связанных объектов:

collection является плейсхолдером для символа, переданного в качестве аргумента name, поэтому has_many :clients добавит, среди прочего, clients.empty?.

collection

Возвращает Relation всех связанных объектов. Если не найдено ни одного объекта, возвращается пустой Relation.

collection<<(object, ...)

Добавляет один или несколько объектов в коллекцию, установив их внешние ключи на первичный ключ коллекции. Обратите внимание, что эта операция немедленно выполняет обновление SQL без ожидания вызова сохранения или обновления родительского объекта, если родительский объект является новым записями. Это также выполнит валидацию и обратные вызовы связанного(ых) объекта(ов).

collection.delete(object, ...)

Удаляет один или несколько объектов из коллекции, установив их внешние ключи в NULL. Объекты также будут уничтожены, если они связаны с dependent: :destroy, и удалены, если они связаны с dependent: :delete_all.

Если используется опция :through, то записи соединения по умолчанию удаляются (а не обнуляются), но вы можете указать dependent: :destroy или dependent: :nullify для переопределения этого.

collection.destroy(object, ...)

Удаляет один или несколько объектов из коллекции, выполнив destroy для каждой записи, независимо от опции зависимостей, гарантируя, что обратные вызовы будут выполнены.

Если используется опция :through, то записи соединения удаляются вместо самих объектов.

collection=objects

Заменяет содержимое коллекции удалением и добавлением объектов по мере необходимости. Если опция :through имеет значение true, то обратные вызовы в моделях соединения вызываются, за исключением обратных вызовов уничтожения, поскольку удаление по умолчанию производится непосредственно. Вы можете указать dependent: :destroy или dependent: :nullify для переопределения этого.

collection_singular_ids

Возвращает массив идентификаторов связанных объектов.

collection_singular_ids=ids

Заменяет коллекцию объектами, идентифицируемыми первичными ключами в ids. Этот метод загружает модели и вызывает collection=. См. выше.

collection.clear

Удаляет все объекты из коллекции. Это уничтожает связанные объекты, если они связаны с dependent: :destroy, удаляет их непосредственно из базы данных, если dependent: :delete_all, в противном случае устанавливает их внешние ключи в NULL. Если опция :through имеет значение true, то обратные вызовы уничтожения не вызываются для моделей соединения. Модели соединения удаляются напрямую.

collection.empty?

Возвращает true если нет связанных объектов.

collection.size

Возвращает количество связанных объектов.

collection.find(...)

Ищет связанный объект в соответствии с теми же правилами, что и ActiveRecord::FinderMethods#find.

collection.exists?(...)

Проверяет, существует ли связанный объект с заданными условиями. Использует те же правила, что и ActiveRecord::FinderMethods#exists?.

collection.build(attributes = {}, ...)

Возвращает один или несколько новых объектов типа коллекции, которые были созданы с attributes и связаны с этим объектом через внешний ключ, но еще не были сохранены.

collection.create(attributes = {})

Возвращает новый объект типа коллекции, который был создан с attributes, связан с этим объектом через внешний ключ и уже сохранен (если он прошёл валидацию). Примечание: Это работает только если базовая модель уже существует в БД, а не если это новая (несохранённая) запись!

collection.create!(attributes = {})

Делает то же, что и collection.create, но поднимает ActiveRecord::RecordInvalid, если запись некорректна.

collection.reload

Возвращает Relation всех связанных объектов, принудительно выполняя чтение из базы данных. Если не найдено ни одного объекта, возвращается пустой Relation.

Пример

class Firm < ActiveRecord::Base
  has_many :clients
end

Объявление has_many :clients добавляет следующие методы (и больше):

firm = Firm.find(2)
client = Client.find(6)

firm.clients                       # similar to Client.where(firm_id: 2)
firm.clients << client
firm.clients.delete(client)
firm.clients.destroy(client)
firm.clients = [client]
firm.client_ids
firm.client_ids = [6]
firm.clients.clear
firm.clients.empty?                # similar to firm.clients.size == 0
firm.clients.size                  # similar to Client.count "firm_id = 2"
firm.clients.find                  # similar to Client.where(firm_id: 2).find(6)
firm.clients.exists?(name: 'ACME') # similar to Client.exists?(name: 'ACME', firm_id: 2)
firm.clients.build                 # similar to Client.new(firm_id: 2)
firm.clients.create                # similar to Client.create(firm_id: 2)
firm.clients.create!               # similar to Client.create!(firm_id: 2)
firm.clients.reload

Объявление также может включать хеш options для специализации поведения ассоциации.

Области видимости

Вы можете передать второй аргумент scope в качестве вызываемого объекта (т.е. proc или lambda) для получения набора записей или для настройки сгенерированного запроса при доступе к связанной коллекции.

Примеры областей видимости:

has_many :comments, -> { where(author_id: 1) }
has_many :employees, -> { joins(:address) }
has_many :posts, ->(blog) { where("max_post_length > ?", blog.max_post_length) }

Расширения

Аргумент extension позволяет передать блок в ассоциацию has_many. Это полезно для добавления новых методов поиска, создания и других методов типа фабрики, которые будут использоваться как часть ассоциации.

Примеры расширений:

has_many :employees do
  def find_or_create_by_name(name)
    first_name, last_name = name.split(" ", 2)
    find_or_create_by(first_name: first_name, last_name: last_name)
  end
end

Параметры

:class_name

Укажите имя класса ассоциации. Используйте его только в том случае, если имя нельзя вывести из имени ассоциации. Так, has_many :products по умолчанию будет связан с классом Product, но если реальное имя класса — SpecialProduct, вам нужно указать его с помощью этого параметра.

:foreign_key

Укажите внешний ключ, используемый для ассоциации. По умолчанию он определяется по имени этого класса в нижнем регистре и суффиксу «_id». Таким образом, класс Person, который создаёт has_many ассоциацию, будет использовать «person_id» в качестве значения по умолчанию :foreign_key.

Установка параметра :foreign_key предотвращает автоматическое обнаружение обратной ассоциации, поэтому рекомендуется установить также :inverse_of.

:foreign_type

Укажите столбец, используемый для хранения типа связанного объекта, если это полиморфная ассоциация. По умолчанию он определяется по имени полиморфной ассоциации, указанной в параметре «as», с суффиксом «_type». Таким образом, класс, который определяет has_many :tags, as: :taggable ассоциацию, будет использовать «taggable_type» в качестве значения по умолчанию :foreign_type.

:primary_key

Укажите имя столбца, используемого в качестве первичного ключа для ассоциации. По умолчанию это id.

:dependent

Управляет тем, что произойдёт со связанными объектами при уничтожении их владельца. Обратите внимание, что эти действия реализованы как колбэки, и Rails выполняет их в определённом порядке. Следовательно, другие подобные колбэки могут повлиять на :dependent поведение, а :dependent поведение может повлиять на другие колбэки.

  • nil ничего не делает (по умолчанию).

  • :destroy приводит к уничтожению всех связанных объектов.

  • :destroy_async уничтожает все связанные объекты в фоновом задании. ПРЕДУПРЕЖДЕНИЕ: Не используйте этот параметр, если ассоциация основана на ограничениях внешнего ключа в вашей базе данных. Действия внешнего ключа будут выполняться в рамках той же транзакции, что и удаление владельца.

  • :delete_all приводит к удалению всех связанных объектов непосредственно из базы данных (так что колбэки не будут выполнены).

  • :nullify приводит к установке внешних ключей в NULL. Тип полиморфного объекта также будет сброшен в полиморфных ассоциациях. Callbacks не выполняются.

  • :restrict_with_exception приводит к возбуждению исключения ActiveRecord::DeleteRestrictionError, если существуют какие-либо связанные записи.

  • :restrict_with_error приводит к добавлению ошибки к владельцу, если есть какие-либо связанные объекты.

Если используется параметр :through, ассоциация в модели соединения должна быть belongs_to, и удаляются записи модели соединения, а не связанные записи.

Если используется dependent: :destroy для ассоциации с ограничением, уничтожаются только объекты с ограничением. Например, если модель Post определяет has_many :comments, -> { where published: true }, dependent: :destroy, и вызывается destroy для поста, уничтожаются только опубликованные комментарии. Это означает, что любые неопубликованные комментарии в базе данных всё ещё будут содержать внешний ключ, указывающий на удалённый пост.

:counter_cache

Этот параметр можно использовать для настройки пользовательского имени :counter_cache. Вам нужен этот параметр только в том случае, если вы изменили имя своего :counter_cache в belongs_to ассоциации.

:as

Указывает полиморфный интерфейс (см. belongs_to).

:through

Указывает ассоциацию, через которую выполняется запрос. Это может быть любой другой тип ассоциации, включая другие :through ассоциации. Параметры :class_name, :primary_key и :foreign_key игнорируются, так как ассоциация использует отражение источника.

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

Если вы собираетесь изменить ассоциацию (а не только читать из неё), рекомендуется установить параметр :inverse_of в исходной ассоциации модели соединения. Это позволяет создавать связанные записи, которые будут автоматически создавать соответствующие записи в модели соединения при сохранении. Подробнее см. Соединительные модели ассоциаций и Настройка обратных связей.

:disable_joins

Указывает, следует ли пропускать соединения для ассоциации. Если значение true, будут сгенерированы две или более запросов. Обратите внимание, что в некоторых случаях, если применяется порядок или ограничение, оно выполняется в памяти из-за ограничений базы данных. Этот параметр применим только к has_many :through ассоциациям, так как has_many сами по себе не выполняют соединения.

:source

Указывает имя ассоциации источника, используемое запросами has_many :through. Используйте его только в том случае, если имя нельзя вывести из ассоциации. has_many :subscribers, through: :subscriptions будет искать :subscribers или :subscriber в Subscription, если не указано :source.

:source_type

Указывает тип ассоциации источника, используемой запросами has_many :through, где ассоциация источника — полиморфная belongs_to.

:validate

При установке в true, проверяет новые объекты, добавленные в ассоциацию, при сохранении родительского объекта. По умолчанию true. Если вы хотите гарантировать повторную проверку связанных объектов при каждом обновлении, используйте validates_associated.

:autosave

Если значение true, всегда сохраняет связанные объекты или уничтожает их, если они помечены на удаление, при сохранении родительского объекта. Если значение false, никогда не сохраняет и не уничтожает связанные объекты. По умолчанию сохраняются только новые связанные объекты. Этот параметр реализован как колбэк before_save. Поскольку колбэки выполняются в порядке их определения, связанные объекты могут потребовать явного сохранения в любом пользовательском before_save колбэке.

Обратите внимание, что NestedAttributes::ClassMethods#accepts_nested_attributes_for устанавливает :autosave в true.

:inverse_of

Указывает имя belongs_to ассоциации в связанном объекте, которая является обратной стороной этой has_many ассоциации. Подробнее см. Взаимные ассоциации.

:extend

Указывает модуль или массив модулей, которые будут расширены в возвращаемый объект ассоциации. Полезно для определения методов в ассоциациях, особенно когда они должны быть общими для нескольких объектов ассоциации.

:strict_loading

При установке в true, всегда загружает связанную запись при загрузке через эту ассоциацию.

:ensuring_owner_was

Служит составным внешним ключом. Определяет список столбцов, используемых для запроса связанного объекта. Это необязательный параметр. По умолчанию Rails попытается получить значение автоматически. Если значение задано, размер Array должен совпадать с первичным ключом модели ассоциированных объектов или размером query_constraints.

:index_errors

Позволяет различать несколько ошибок валидации из записей ассоциации, включая индекс в имени атрибута ошибки, например, roles[2].level. При установке в true, индекс основан на порядке ассоциации, то есть порядке базы данных, а новые записи, которые ещё не сохранены, размещаются в конце. При установке в :strict_loading, индекс основан на порядке записей, полученных обработчиком вложенных атрибутов при использовании accepts_nested_attributes_for.

:before_add

Определяет колбэк ассоциации, который срабатывает перед добавлением объекта в коллекцию ассоциации.

:after_add

Определяет колбэк ассоциации, который срабатывает после добавления объекта в коллекцию ассоциации.

:before_remove

Определяет колбэк ассоциации, который срабатывает перед удалением объекта из коллекции ассоциации.

:after_remove

Определяет колбэк ассоциации, который срабатывает после удаления объекта из коллекции ассоциации.

Примеры параметров:

has_many :comments, -> { order("posted_on") }
has_many :comments, -> { includes(:author) }
has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
has_many :tracks, -> { order("position") }, dependent: :destroy
has_many :comments, dependent: :nullify
has_many :tags, as: :taggable
has_many :reports, -> { readonly }
has_many :subscribers, through: :subscriptions, source: :user
has_many :subscribers, through: :subscriptions, disable_joins: true
has_many :comments, strict_loading: true
has_many :comments, query_constraints: [:blog_id, :post_id]
has_many :comments, index_errors: :nested_attributes_order
has_one(name, scope = nil, **options) Показать исходный код
# File activerecord/lib/active_record/associations.rb, line 1498
def has_one(name, scope = nil, **options)
  reflection = Builder::HasOne.build(self, name, scope, options)
  Reflection.add_reflection self, name, reflection
end

Устанавливает одно-к-одному ассоциацию с другим классом. Этот метод следует использовать только в том случае, если другой класс содержит внешний ключ. Если текущий класс содержит внешний ключ, то следует использовать belongs_to вместо него. Подробности о том, когда использовать has_one, а когда belongs_to, см. в разделе Is it a belongs_to or has_one association?.

Будут добавлены следующие методы для получения и запроса одного связанного объекта:

association — это плейсхолдер для символа, переданного в качестве аргумента name, поэтому has_one :manager, помимо прочего, добавит manager.nil?.

association

Возвращает связанный объект. Если объект не найден, возвращается nil.

association=(associate)

Присваивает связанный объект, извлекает первичный ключ, устанавливает его в качестве внешнего ключа и сохраняет связанный объект. Для предотвращения несоответствий в базе данных при присвоении нового связанного объекта, существующий связанный объект постоянно удаляется, даже если новый объект не сохранен в базе данных.

build_association(attributes = {})

Возвращает новый объект связанного типа, который был создан с attributes и связан с этим объектом через внешний ключ, но еще не сохранен.

create_association(attributes = {})

Возвращает новый объект связанного типа, который был создан с attributes, связан с этим объектом через внешний ключ и уже сохранен (если прошел валидацию).

create_association!(attributes = {})

Делает то же, что и create_association, но вызывает ActiveRecord::RecordInvalid, если запись недействительна.

reload_association

Возвращает связанный объект, принудительно выполняя чтение из базы данных.

reset_association

Разгружает связанный объект. Следующий доступ запросит его из базы данных.

Пример

class Account < ActiveRecord::Base
  has_one :beneficiary
end

Объявление has_one :beneficiary добавляет следующие методы (и другие):

account = Account.find(5)
beneficiary = Beneficiary.find(8)

account.beneficiary               # similar to Beneficiary.find_by(account_id: 5)
account.beneficiary = beneficiary # similar to beneficiary.update(account_id: 5)
account.build_beneficiary         # similar to Beneficiary.new(account_id: 5)
account.create_beneficiary        # similar to Beneficiary.create(account_id: 5)
account.create_beneficiary!       # similar to Beneficiary.create!(account_id: 5)
account.reload_beneficiary
account.reset_beneficiary

Области

Вы можете передать второй аргумент scope в качестве вызываемого объекта (например, proc или lambda), чтобы получить определенную запись или настроить генерируемый запрос при доступе к связанному объекту.

Примеры областей:

has_one :author, -> { where(comment_id: 1) }
has_one :employer, -> { joins(:company) }
has_one :latest_post, ->(blog) { where("created_at > ?", blog.enabled_at) }

Параметры

Объявление также может включать options хэш для специализации поведения ассоциации.

Параметры:

:class_name

Укажите имя класса ассоциации. Используйте его только в том случае, если имя нельзя вывести из имени ассоциации. Так, has_one :manager по умолчанию будет связана с классом Manager, но если фактическое имя класса — Person, вам нужно указать его с помощью этого параметра.

:dependent

Управляет тем, что происходит с ассоциированным объектом, когда уничтожается его владелец:

  • nil ничего не делать (по умолчанию).

  • :destroy приводит к уничтожению ассоциированного объекта.

  • :destroy_async приводит к уничтожению ассоциированного объекта в фоновом задании. ВНИМАНИЕ: Не используйте этот параметр, если ассоциация поддерживается ограничениями внешнего ключа в вашей базе данных. Действия ограничения внешнего ключа будут выполняться в одной транзакции, которая удаляет его владельца.

  • :delete приводит к прямому удалению ассоциированного объекта из базы данных (так что обратные вызовы не будут выполнены).

  • :nullify приводит к установке внешнего ключа в NULL. Также обнуляется столбец полиморфного типа для полиморфных ассоциаций. Обратные вызовы Callbacks не выполняются.

  • :restrict_with_exception приводит к тому, что будет выброшено исключение ActiveRecord::DeleteRestrictionError, если существует связанная запись.

  • :restrict_with_error приводит к добавлению ошибки к владельцу, если существует связанный объект.

Обратите внимание, что параметр :dependent игнорируется при использовании параметра :through.

:foreign_key

Укажите внешний ключ, используемый для ассоциации. По умолчанию он определяется по имени этого класса в нижнем регистре и с добавленным суффиксом «_id». Таким образом, класс Person, который создаёт ассоциацию has_one, будет использовать «person_id» в качестве значения по умолчанию :foreign_key.

Установка параметра :foreign_key предотвращает автоматическое обнаружение обратной ассоциации, поэтому рекомендуется также установить параметр :inverse_of.

:foreign_type

Укажите столбец, используемый для хранения типа ассоциированного объекта, если это полиморфная ассоциация. По умолчанию он определяется по имени полиморфной ассоциации, указанной в параметре «as», с добавленным суффиксом «_type». Таким образом, класс, определяющий ассоциацию has_one :tag, as: :taggable, будет использовать «taggable_type» в качестве значения по умолчанию :foreign_type.

:primary_key

Укажите метод, возвращающий первичный ключ, используемый для ассоциации. По умолчанию это id.

:as

Указывает полиморфный интерфейс (см. belongs_to).

:through

Указывает модель объединения, через которую выполняется запрос. Параметры :class_name, :primary_key, и :foreign_key игнорируются, так как ассоциация использует рефлексию источника. Вы можете использовать запрос :through только через ассоциацию has_one или belongs_to в модели объединения.

Если ассоциация в модели объединения — belongs_to, коллекция может быть изменена, и записи в модели :through будут автоматически создаваться и удаляться при необходимости. В противном случае коллекция является только для чтения, поэтому вы должны напрямую управлять ассоциацией :through.

Если вы собираетесь изменять ассоциацию (а не только читать из неё), рекомендуется установить параметр :inverse_of в исходной ассоциации в модели объединения. Это позволяет создавать связанные записи, которые будут автоматически создавать соответствующие записи в модели объединения при сохранении. Смотрите Ассоциации с моделями объединения и Установка обратных ссылок для получения более подробной информации.

:disable_joins

Указывает, нужно ли пропускать соединения для ассоциации. Если установлено в true, будут сгенерированы две или более запросов. Обратите внимание, что в некоторых случаях, если применены сортировка или ограничение, они будут выполнены в памяти из-за ограничений базы данных. Этот параметр применим только к ассоциациям has_one :through, так как has_one в одиночку не выполняет соединение.

:source

Указывает имя ассоциации источника, используемое запросами has_one :through. Используйте его только если имя нельзя вывести из ассоциации. has_one :favorite, through: :favorites будет искать :favorite в Favorite, если не указано :source.

:source_type

Указывает тип ассоциации источника, используемой запросами has_one :through , где ассоциация источника — полиморфная belongs_to.

:validate

Если установлено в true, валидирует новые объекты, добавленные в ассоциацию, при сохранении родительского объекта. false по умолчанию. Если вы хотите убедиться, что связанные объекты перевалидируются при каждом обновлении, используйте validates_associated.

:autosave

Если true, всегда сохраняет или уничтожает ассоциированный объект, если он помечен на уничтожение, при сохранении родительского объекта. Если false, никогда не сохраняет и не уничтожает ассоциированный объект.

По умолчанию сохраняет только ассоциированный объект, если это новая запись. Установка этого параметра в true также включает валидацию ассоциированных объектов, если явным образом не отключена с помощью validate: false . Это связано с тем, что сохранение объекта с недопустимыми ассоциированными объектами завершится неудачей, поэтому любые связанные объекты пройдут проверки валидации.

Обратите внимание, что NestedAttributes::ClassMethods#accepts_nested_attributes_for устанавливает :autosave в true.

:touch

Если true, ассоциированный объект будет отмечен (атрибуты updated_at / updated_on установлены на текущее время) при сохранении или удалении этой записи. Если вы укажите символ, этот атрибут будет обновлён текущим временем, помимо атрибутов updated_at / updated_on. Обратите внимание, что при отмеченнии валидация не выполняется, и будут выполнены только обратные вызовы after_touch, after_commit, и after_rollback.

:inverse_of

Указывает имя ассоциации belongs_to в ассоциированном объекте, которая является обратной к этой ассоциации has_one. Подробнее см. Взаимонаправленные ассоциации.

:required

Если установлено в true, ассоциация также будет проходить проверку наличия. Эта проверка будет выполняться для самой ассоциации, а не для ID. Вы можете использовать :inverse_of, чтобы избежать дополнительного запроса во время проверки.

:strict_loading

Принудительно загружает связанную запись каждый раз, когда она загружается через эту ассоциацию.

:ensuring_owner_was

Указывает метод, который нужно вызвать у владельца. Метод должен возвращать true, чтобы связанные записи могли быть удалены в фоновом задании.

:query_constraints

Служит составным внешним ключом. Определяет список столбцов, которые нужно использовать для запроса ассоциированного объекта. Это необязательный параметр. По умолчанию Rails попытается определить значение автоматически. При установке значения размер Array должен соответствовать первичному ключу связанной модели или размеру query_constraints.

Примеры параметров:

has_one :credit_card, dependent: :destroy  # destroys the associated credit card
has_one :credit_card, dependent: :nullify  # updates the associated records foreign
                                              # key value to NULL rather than destroying it
has_one :last_comment, -> { order('posted_on') }, class_name: "Comment"
has_one :project_manager, -> { where(role: 'project_manager') }, class_name: "Person"
has_one :attachment, as: :attachable
has_one :boss, -> { readonly }
has_one :club, through: :membership
has_one :club, through: :membership, disable_joins: true
has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
has_one :credit_card, required: true
has_one :credit_card, strict_loading: true
has_one :employment_record_book, query_constraints: [:organization_id, :employee_id]

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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