Что делает инверсия? Какой 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

8 ответов


С документация, похоже на :inverse_of option-это способ избежать 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 объект вместо загрузки нового, как это было бы по умолчанию.


Я думаю :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 данные извлекаются из памяти.


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

руководство

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

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

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.автор-это два разных представления в памяти одних и тех же данных, и ни одно из них не обновляется автоматически от изменений к другому. Active Record предоставляет параметр: inverse_of, чтобы вы могли сообщить ему об этих отношениях:

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

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

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

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

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


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

Итак, если вы используете пример 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-запросов


когда у нас есть 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



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


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

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

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

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

ассоциации типа has_many belongs_to обратную связь игнорируются.