Что делает inverse_of? Какой SQL он генерирует?

Я пытаюсь обойти inverse_of, и я не понимаю.

Как выглядит сгенерированный sql, если он есть?

Имеет ли параметр inverse_of такое же поведение, если оно используется с :has_many, :belongs_to и :has_many_and_belongs_to?

Извините, если это такой основной вопрос.

Я видел этот пример:

class Player < ActiveRecord::Base
  has_many :cards, :inverse_of => :player
end

class Card < ActiveRecord::Base
  belongs_to :player, :inverse_of => :cards
end

Ответ 1

Из документации кажется, что опция :inverse_of - это метод, позволяющий избежать SQL-запросов, а не генерировать их. Это подсказка к ActiveRecord, чтобы использовать уже загруженные данные вместо того, чтобы снова извлекать их через отношения.

Пример:

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

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

Ответ 2

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

class Project < ActiveRecord::Base
  has_many :tasks, :inverse_of=>:project
end

class Task < ActiveRecord::Base
  belongs_to :project, :inverse_of=>:tasks
end

Теперь, в консоли:

irb> p = Project.new
=> #<Project id: nil, name: nil, ...>
irb> t = p.tasks.build
=> #<Task id: nil, project_id: nil, ...>
irb> t.project
=> #<Project id: nil, name: nil, ...>

Без аргументов: inverse_of, t.project вернет nil, потому что он вызывает запрос sql, и данные еще не сохранены. С аргументами: inverse_of данные извлекаются из памяти.

Ответ 3

Из документации Rails 5.0 и отлично.

Guide

Двунаправленные ассоциации

Для ассоциаций нормально работать в двух направлениях, требуя объявления на двух разных моделях:

class Author < ApplicationRecord
  has_many :books
end

class Book < ApplicationRecord
  belongs_to :author
end

По умолчанию Active Record не знает о соединении между этими ассоциациями. Это может привести к тому, что две копии объекта не синхронизируются:

a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'Manny'
a.first_name == b.author.first_name # => false

Это происходит из-за того, что a и b.author представляют собой два разных представления в одной и той же информации в памяти, и ни один из них не обновляется автоматически. Активная запись предоставляет опцию: inverse_of, чтобы вы могли сообщить ей об этих отношениях:

class Author < ApplicationRecord
  has_many :books, inverse_of: :author
end

class Book < ApplicationRecord
  belongs_to :author, inverse_of: :books
end

При этих изменениях Active Record загружает только одну копию объекта автора, предотвращая несогласованности и делая ваше приложение более эффективным:

a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'Manny'
a.first_name == b.author.first_name # => true

Есть несколько ограничений на поддержку inverse_of:

Они не работают: через ассоциации. Они не работают с: полиморфными ассоциациями. Они не работают с: как ассоциации.

Для ассоциаций belongs_to has_many обратные ассоциации игнорируются. Каждая ассоциация будет пытаться автоматически найти обратную связь и задать эвристический вариант: inverse_of (на основе имени ассоциации). Большинство ассоциаций со стандартными именами будут поддерживаться. Однако ассоциации, которые содержат следующие параметры, не будут автоматически устанавливать свои инверсии:

  • : условия
  • : через
  • : полиморфный
  • : foreign_key

Ответ 4

Просто обновление для всех - мы только что использовали inverse_of с одним из наших приложений с ассоциацией has_many :through


В основном это делает объект "origin" доступным для "дочернего" объекта

Итак, если вы используете пример Rails:

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
  validates :id,
      :presence => { :message => "Dungeon ID Required", :unless => :draft? }

  private
  def draft?
      self.dungeon.draft
  end 
end

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

Использование :inverse_of позволит вам получить доступ к объекту данных, в котором он обратный, без выполнения дальнейших SQL-запросов

Ответ 5

Когда у нас есть 2 модели с отношениями has_many и belongs_to, всегда лучше использовать inverse_of, которые информируют ActiveRecod о том, что они принадлежат к одной стороне ассоциации. Поэтому, если запрос с одной стороны запущен, он будет кэшировать и обслуживать кеш, если он запускается с противоположного направления. Что улучшает производительность. Из Rails 4.1 функция inverse_of будет установлена ​​автоматически, если мы используем foreign_key или изменения имени класса, нам нужно явно указать.

Лучшая статья для деталей и примеров.

http://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations

Ответ 6

Если у вас есть отношение has_many_through между двумя моделями, "Пользователь и роль", и вы хотите проверить соответствие модели назначения "Назначение" на несуществующие или недействительные записи с помощью validates_presence of :user_id, :role_id, это полезно. Вы все равно можете создать User @user с его ассоциацией @user.role(params[:role_id]), чтобы сохранить пользователя не приведет к отказу проверки модели присваивания.

Ответ 8

Пожалуйста, посмотрите два полезных ресурса

И помните некоторые ограничения inverse_of:

не работает с: через ассоциации.

не работает с: полиморфными ассоциациями.

для ассоциаций belongs_to has_many обратные ассоциации игнорируются.