Что делает инверсия? Какой 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
взгляните на эту статью!!
http://gsusmonzon.blogspot.com.br/2011/09/rails-power-of-inverseof.html
Если у вас has_many_through
связь между двумя моделями, Пользователь и роль, и хотите проверить назначение соединительной модели против несуществующих или недопустимых записей с validates_presence of :user_id, :role_id
, это полезно. Вы все еще можете создать пользователя @user с его ассоциацией @user.role(params[:role_id])
чтобы сохранение пользователя не привело к сбою проверки модели назначения.
пожалуйста, посмотрите 2 два полезных ресурса
- https://www.viget.com/articles/exploring-the-inverse-of-option-on-rails-model-associations
- http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Bi-directional+associations
и помните некоторые ограничения inverse_of
:
не работает :через ассоциации.
не работает с :полиморфные ассоциации.
ассоциации типа has_many belongs_to обратную связь игнорируются.