Co robi inverse_of? Jaki SQL generuje?

143

Próbuję się rozejrzeć inverse_ofi nie rozumiem.

Jak wygląda wygenerowany plik sql, jeśli w ogóle?

Czy inverse_ofopcja wykazują takie samo zachowanie, jeśli stosować :has_many, :belongs_toi :has_many_and_belongs_to?

Przepraszam, jeśli to takie podstawowe pytanie.

Widziałem ten przykład:

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

class Card < ActiveRecord::Base
  belongs_to :player, :inverse_of => :cards
end
Federico
źródło

Odpowiedzi:

125

Z dokumentacji wynika , że :inverse_ofopcja jest sposobem na uniknięcie zapytań SQL, a nie ich generowanie. Jest to wskazówka dla ActiveRecord, aby używać już załadowanych danych zamiast pobierać je ponownie za pośrednictwem relacji.

Ich przykład:

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

W takim przypadku wywołanie dungeon.traps.first.dungeonpowinno zwrócić oryginalny dungeonobiekt zamiast ładować nowy, jak byłoby to w przypadku domyślnym.

tadman
źródło
5
Czy rozumiesz komentarz w dokumentacji: „w przypadku asocjacji nalezy_do ma_wielu asocjacji odwrotnych jest ignorowanych.”. A jednak dokument używa tego dokładnego przykładu. Czego tu brakuje?
dynex
50
To wszystko jest dla mnie bardzo dziwne, ponieważ wydaje mi się, że zawsze chciałbyś tego zachowania domyślnie i musisz użyć: inverse_of tylko wtedy, gdy nie można wywnioskować nazwy skojarzenia. Uciążliwe są również niespójności w definicji, ale w kilku przypadkach pomogło mi to. Czy jest jakiś powód, dla którego nie powinienem go wszędzie trzymać?
Ibrahim,
18
@Ibrahim Sprawdź to, zostało połączone 23 dni temu! github.com/rails/rails/pull/9522
Hut8
6
Zignorowanie odwrotności asocjacji przynależy_to ma sens, ponieważ nie ma gwarancji, że rekord potomny rekordu A będzie rekordem A - może to być element równorzędny rekordu A. Jednak rodzic elementu podrzędnego rekordu A , gwarantuje rekord A.
David Aldridge
2
Przyszły czytelnik może uzyskać pomoc z tego bloga ...: D
Arup Rakshit
42

Myślę, że :inverse_ofjest to najbardziej przydatne, gdy pracujesz ze skojarzeniami, które nie zostały jeszcze utrwalone. Na przykład:

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

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

Teraz w konsoli:

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, ...>

Bez :inverse_ofargumentów t.projectzwróci nil, ponieważ wyzwala zapytanie sql, a dane nie są jeszcze zapisane. Wraz z :inverse_ofargumentami dane są pobierane z pamięci.

KenB
źródło
1
Miałem problem z accepts_nested_attributes_for. Domyślnie wyświetlane są tylko zagnieżdżone atrybuty istniejących skojarzonych obiektów (akcja edycji). Jeśli, na przykład, chcesz TWORZYĆ obiekt z, powiedzmy, 3 powiązanymi obiektami, powinieneś mieć w swoich modelach Model.new (nowa akcja) i: inverse_of.
Victor Marconi
Zgoda co do zachowania w Rails 4 i późniejszych, ale działała dobrze w wersji 3 (z wyjątkiem niektórych późniejszych wcieleń, chociaż stara składnia działa ponownie w wersji 3.2.13). Zwróć uwagę, że w modelu łączenia nie można już sprawdzać obecności identyfikatorów - tylko model-obiekt. Wygląda na to, że możesz mieć skojarzenie bez identyfikatora, w `` logice '' v4.
JosephK
Dokładnie ... :inverse_ofrozwiązałem problem podczas tworzenia nowych obiektów nadrzędnych i podrzędnych w tej samej formie.
WM
16

Po tym pr ( https://github.com/rails/rails/pull/9522 ) inverse_of nie jest wymagane w większości przypadków.

Moduł Active Record obsługuje automatyczną identyfikację większości skojarzeń ze standardowymi nazwami. Jednak moduł Active Record nie zidentyfikuje automatycznie powiązań dwukierunkowych, które zawierają zakres lub którąkolwiek z następujących opcji:

  • :przez
  • : klucz_obcy
class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => true

W powyższym przykładzie odwołanie do tego samego obiektu jest przechowywane w zmiennej aoraz w atrybucie writer.

artamonovdev
źródło
Używam Rails 5 i niezależnie od tego, czy dodasz, inverse_ofczy nie, wynik dla a.first_name == b.author.first_namejest zawsze właściwy.
Arslan Ali
@ArslanAli dzięki za wspaniały komentarz, zaktualizowałem odpowiedź.
artamonovdev
5

Tylko aktualizacja dla wszystkich - właśnie używaliśmy inverse_ofjednej z naszych aplikacji z has_many :throughpowiązaniem


Zasadniczo udostępnia obiekt „pochodzenie” obiektowi „potomnemu”

Więc jeśli używasz przykładu Railsów:

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

Użycie :inverse_ofumożliwia dostęp do obiektu danych, którego jest odwrotnością, bez wykonywania dalszych zapytań SQL

Richard Peck
źródło
5

Kiedy mamy 2 modele z relacjami has_many i contrib_to, zawsze lepiej jest użyć inverse_of, które informują ActiveRecod, że należą do tej samej strony asocjacji. Więc jeśli zostanie wyzwolone zapytanie z jednej strony, będzie buforować i wyświetlać z pamięci podręcznej, jeśli zostanie wyzwolone z przeciwnego kierunku. Co poprawia wydajność. Od Rails 4.1, inverse_of zostanie ustawione automatycznie, jeśli użyjemy obcego_klucza lub zmian w nazwie klasy musimy ustawić jawnie.

Najlepszy artykuł ze szczegółami i przykładami.

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

Gurudath BN
źródło
3

Jeśli masz has_many_throughrelację między dwoma modelami, użytkownikiem i rolą, i chcesz zweryfikować przypisanie modelu łączącego pod kątem nieistniejących lub nieprawidłowych wpisów za pomocą validates_presence of :user_id, :role_id, jest to przydatne. Nadal można wygenerować użytkownika @user z jego skojarzeniem @user.role(params[:role_id]), aby zapisanie użytkownika nie spowodowało niepowodzenia walidacji modelu przypisania.

Informatom
źródło
-1

Proszę spojrzeć na 2 dwa przydatne zasoby

I pamiętaj o ograniczeniach inverse_of:

nie działa z: poprzez skojarzenia.

nie działa z: skojarzeniami polimorficznymi.

dla przypisań należą_to ma_wiele asocjacji odwrotnych jest ignorowanych.

Leo Le
źródło