Что делает 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])
, чтобы сохранить пользователя не приведет к отказу проверки модели присваивания.
Ответ 7
Взгляните на эту статью!
http://gsusmonzon.blogspot.com.br/2011/09/rails-power-of-inverseof.html
Ответ 8
Пожалуйста, посмотрите два полезных ресурса
И помните некоторые ограничения inverse_of
:
не работает с: через ассоциации.
не работает с: полиморфными ассоциациями.
для ассоциаций belongs_to has_many обратные ассоциации игнорируются.