2012-02-15 22 views
109

Próbuję uzyskać moją głowę wokół inverse_of i nie rozumiem.Co robi odwrotność? Jakie SQL to generuje?

Jak wygląda wygenerowany sql?

Czy opcja inverse_of wykazuje takie samo zachowanie, jeśli jest używana z :has_many, :belongs_to i :has_many_and_belongs_to?

Przepraszam, jeśli to jest 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 

Odpowiedz

98

Od the documentation, wydaje się opcją :inverse_of to metoda unikania zapytań SQL, a nie ich generowania. Jest to wskazówka dla ActiveRecord, aby używał już załadowanych danych zamiast pobierać je ponownie przez relację.

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 tym przypadku, nazywając dungeon.traps.first.dungeon powinien powrócić oryginalnego dungeon obiekt zamiast wczytywania nowego jak byłoby w przypadku domyślnie.

+5

Czy rozumiesz komentarz w dokumentacji: "dla belongs_to association has_many asocjacji odwrotnych są ignorowane.". A jednak doktor używa tego dokładnego przykładu. Czego tu mi brakuje? – dynex

+1

Nie jestem do końca pewien. 'inverse_of' nie ma zbyt dużego zastosowania, więc może to być zachowanie, które nie jest dobrze zdefiniowane. Próbowałem go używać po wskazaniu tej funkcji i tak naprawdę nie zrobiło dla mnie nic pożytecznego. – tadman

+46

To wszystko jest dla mnie bardzo dziwne, ponieważ wydaje mi się, że zawsze chcesz tego zachowania domyślnie, i tylko trzeba użyć: inverse_of, gdy nazwa skojarzenia nie może być wywnioskowane. Również niespójności w definicji są uciążliwe, ale pomogło mi to w kilku przypadkach. Jakiś powód, dla którego nie powinienem trzymać go wszędzie? – Ibrahim

3

Jeśli masz relację has_many_through między dwoma modelami, użytkownikiem i rolą i chcesz sprawdzić poprawność przypisania modelu połączenia do nieistniejących lub niepoprawnych wpisów za pomocą validates_presence of :user_id, :role_id, jest to przydatne. Nadal możesz wygenerować użytkownika @ użytkownik z jego powiązaniem @user.role(params[:role_id]), aby zapisanie użytkownika nie spowodowało niepowodzenia sprawdzania poprawności modelu przypisania.

5

właśnie aktualizacja dla wszystkich - po prostu skorzystaliśmy inverse_of z jednym z naszych aplikacji z stowarzyszenia has_many :through


To w zasadzie sprawia, że ​​obiekt „pochodzenie” dostępną na „dziecko” obiektu

Więc jeśli używasz przykład szyn:

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 

Korzystanie :inverse_of pozwoli Ci uzyskać dostęp do obiektu danych, który jest odwrotnością, bez wykonywania żadnych dodatkowych zapytań SQL

30

myślę :inverse_of jest najbardziej przydatny podczas pracy ze stowarzyszeniami, które nie zostały jeszcze trwało. Np .:

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_of argumentów, by t.project powrócić zerowa, ponieważ uruchamia zapytanie SQL i dane nie są jeszcze zapisane. Przy argumentach: inverse_of dane są pobierane z pamięci.

+0

Miałem problem z accepts_nested_attributes_for. Domyślnie wyświetlane są tylko zagnieżdżone atrybuty dla istniejących powiązanych obiektów (akcja edycji). Jeśli na przykład chcesz utworzyć obiekt z, powiedzmy, 3 powiązanymi obiektami, powinieneś mieć Model.new (nowa akcja) i: inverse_of w swoich modelach. –

+0

Uzgodniono zachowanie w Railsach 4 i późniejszych, ale działało dobrze w wersji v3 (z wyjątkiem niektórych późniejszych wcieleń, chociaż stara składnia działa ponownie w wersji 3.2.13). Uwaga w modelu łączenia nie może potwierdzić obecności identyfikatorów - tylko model-obiekt. Wygląda na to, że możesz mieć skojarzenie bez identyfikatora w logice v4. – JosephK

+0

Dokładnie .. ': inverse_of' rozwiązał problem podczas tworzenia nowych elementów nadrzędnych i podrzędnych w tej samej formie. –

4

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

Najlepszy artykuł na szczegóły i przykład.

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

7

Z dokumentacji Rails 5.0 i wielkich.

Guide

Dwukierunkowy stowarzyszenia

To normalne dla stowarzyszenia do pracy w dwóch kierunkach, wymagające deklaracji na dwóch różnych modelach:

class Author < ApplicationRecord 
    has_many :books 
end 

class Book < ApplicationRecord 
    belongs_to :author 
end 

Domyślnie Active Record robi” t wiedzieć o związku między tymi powiązaniami. Może to prowadzić do dwóch kopii obiektu coraz zsynchronizowane:

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 

Dzieje się tak dlatego, że i b.author są dwie reprezentacje różnią się w pamięci z tych samych danych, a żadna z nich jest automatycznie odświeżany od zmian inny. Active Record zapewnia: inverse_of opcję, aby można było poinformować ją o tych relacjach:

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

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

z tymi zmianami, Active Record będzie ładować tylko jedną kopię obiektu autora, zapobiegając niespójności i co aplikacja bardziej efektywne:

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 

Istnieje kilka ograniczeń do inverse_of wsparcia:

one nie działają: poprzez stowarzyszenia. Nie działają z: stowarzyszenia polimorficzne. Nie działają z: as association.

Dla asocjacji belongs_to, has_many asocjacji odwrotnych są ignorowane. Każde skojarzenie będzie próbowało automatycznie znaleźć powiązanie odwrotne i ustawić opcję: inverse_of heurystycznie (na podstawie nazwy asocjacji). Większość skojarzeń z nazwami standardowymi będzie obsługiwana.Jednak związki, które zawierają następujące opcje nie będą już ich odwrotności ustawić automatycznie:

  • : warunki
  • : poprzez
  • : polimorficznych
  • : foreign_key
+0

Używam Rails 5, i albo dodajesz 'inverse_of', albo nie, wynikiem dla' a.name = = b.author.first_name' jest zawsze ture. –

-1

Proszę spojrzeć 2 dwa użyteczne zasoby:

i zapamiętać pewne ograniczenia inverse_of:

nie działa: poprzez stowarzyszenia.

nie działa z: stowarzyszeniami polimorficznymi.

dla asocjacji belongs_to has_many asocjacji odwrotnych są ignorowane.