Was macht inverse_of? Welches SQL wird generiert?

143

Ich versuche meinen Kopf herumzukriegen inverse_ofund ich verstehe es nicht.

Wie sieht die generierte SQL aus, wenn überhaupt?

Hat die inverse_ofzeigen Option das gleiche Verhalten verwendet , wenn sie mit :has_many, :belongs_tound :has_many_and_belongs_to?

Entschuldigung, wenn dies eine so grundlegende Frage ist.

Ich habe dieses Beispiel gesehen:

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

class Card < ActiveRecord::Base
  belongs_to :player, :inverse_of => :cards
end
Federico
quelle

Antworten:

125

Aus der Dokumentation geht hervor , dass die :inverse_ofOption eine Methode ist, um SQL-Abfragen zu vermeiden und nicht zu generieren. Es ist ein Hinweis für ActiveRecord, bereits geladene Daten zu verwenden, anstatt sie erneut über eine Beziehung abzurufen.

Ihr Beispiel:

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

In diesem Fall dungeon.traps.first.dungeonsollte der Aufruf das ursprüngliche dungeonObjekt zurückgeben, anstatt ein neues zu laden, wie dies standardmäßig der Fall wäre.

Tadman
quelle
5
Verstehen Sie den Kommentar in der Dokumentation: "Für Zugehörigkeiten zu Assoziationen hat viele umgekehrte Assoziationen ignoriert." Und doch verwendet das Dokument genau dieses Beispiel. Was fehlt mir hier?
Dynex
50
Das ist alles sehr seltsam für mich, weil es mir so scheint, als würden Sie dieses Verhalten immer standardmäßig wollen und müssen nur Folgendes verwenden: inverse_of, wenn der Assoziationsname nicht abgeleitet werden kann. Auch die Inkonsistenzen in der Definition sind störend, aber es hat mir in einigen Fällen geholfen. Gibt es einen Grund, warum ich es nicht überall festhalten sollte?
Ibrahim
18
@Ibrahim Check this out, es wurde vor 23 Tagen zusammengeführt! github.com/rails/rails/pull/9522
Hut8
6
Es ist sinnvoll, die Umkehrung einer Zugehörigkeitszuordnung zu ignorieren, da nicht garantiert wird, dass das untergeordnete Element des übergeordneten Datensatzes von Datensatz A Datensatz A ist - es könnte sich jedoch um ein Geschwister von Datensatz A handeln. Das übergeordnete Element eines untergeordneten Datensatzes von Datensatz A. , ist garantiert Rekord A.
David Aldridge
2
Zukünftige Leser könnten Hilfe von diesem Blog bekommen ...: D
Arup Rakshit
42

Ich denke, es :inverse_ofist am nützlichsten, wenn Sie mit Assoziationen arbeiten, die noch nicht bestehen. Z.B:

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

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

Nun in der Konsole:

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

Ohne die :inverse_ofArgumente t.projectwürde zurückgegeben nil, da es eine SQL-Abfrage auslöst und die Daten noch nicht gespeichert sind. Mit den :inverse_ofArgumenten werden die Daten aus dem Speicher abgerufen.

KenB
quelle
1
Ich hatte ein Problem mit accept_nested_attributes_for. Standardmäßig werden nur verschachtelte Attribute für vorhandene zugeordnete Objekte angezeigt (Aktion bearbeiten). Wenn Sie beispielsweise ein Objekt mit beispielsweise 3 zugeordneten Objekten ERSTELLEN möchten, sollten Sie Model.new (neue Aktion) und: inverse_of in Ihren Modellen haben.
Victor Marconi
Einverstanden über das Verhalten in Rails 4 und höher, aber es funktionierte in Version 3 einwandfrei (mit Ausnahme einiger späterer Inkarnationen, obwohl die alte Syntax in Version 3.2.13 wieder funktioniert). Und beachten Sie, dass im Join-Modell das Vorhandensein der IDs nicht mehr überprüft werden kann - nur das Modellobjekt. Anscheinend können Sie in v4 'Logik' eine Zuordnung ohne ID haben.
JosephK
Genau .. hat :inverse_ofein Problem für mich beim Erstellen neuer übergeordneter und untergeordneter Entitäten in derselben Form gelöst.
WM
16

Nach diesem PR ( https://github.com/rails/rails/pull/9522 ) ist inverse_of in den meisten Fällen nicht erforderlich.

Active Record unterstützt die automatische Identifizierung für die meisten Zuordnungen mit Standardnamen. Active Record erkennt jedoch nicht automatisch bidirektionale Zuordnungen, die einen Bereich oder eine der folgenden Optionen enthalten:

  • :durch
  • :Unbekannter Schlüssel
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

Im obigen Beispiel wird ein Verweis auf dasselbe Objekt in der Variablen aund im Attribut gespeichert writer.

artamonovdev
quelle
Ich verwende Rails 5, und entweder Sie hinzufügen inverse_ofoder nicht, das Ergebnis für a.first_name == b.author.first_nameist immer sicher.
Arslan Ali
@ArslanAli danke für den tollen Kommentar, ich habe die Antwort aktualisiert.
Artamonovdev
5

Nur ein Update für alle - wir haben es nur inverse_ofmit einer unserer Apps mit einer has_many :throughAssoziation verwendet


Grundsätzlich stellt es das "Ursprungs" -Objekt dem "untergeordneten" Objekt zur Verfügung

Wenn Sie also das Beispiel von Rails verwenden:

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

Mit :inverse_ofkönnen Sie auf das Datenobjekt zugreifen, von dem es umgekehrt ist, ohne weitere SQL-Abfragen ausführen zu müssen

Richard Peck
quelle
5

Wenn wir zwei Modelle mit has_many und Zugehörigkeit zu Beziehung haben, ist es immer besser, inverse_of zu verwenden, die ActiveRecod darüber informieren, dass sie zur selben Seite der Zuordnung gehören. Wenn also eine Abfrage von einer Seite ausgelöst wird, wird sie zwischengespeichert und aus dem Cache bereitgestellt, wenn sie aus der entgegengesetzten Richtung ausgelöst wird. Was die Leistung verbessert. Ab Rails 4.1 wird inverse_of automatisch festgelegt. Wenn wir den Schlüssel fremd verwenden oder den Klassennamen ändern, müssen wir ihn explizit festlegen.

Bester Artikel für Details und Beispiel.

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

Gurudath BN
quelle
3

Wenn Sie eine has_many_throughBeziehung zwischen zwei Modellen, Benutzer und Rolle, haben und die Zuordnung des Verbindungsmodells für nicht vorhandene oder ungültige Einträge mit validieren möchten validates_presence of :user_id, :role_id, ist dies hilfreich. Sie können weiterhin einen User @user mit seiner Zuordnung generieren, @user.role(params[:role_id])damit das Speichern des Benutzers nicht zu einer fehlgeschlagenen Validierung des Zuweisungsmodells führt.

Informatom
quelle
-1

Bitte werfen Sie einen Blick auf zwei nützliche Ressourcen

Und denken Sie an einige Einschränkungen von inverse_of:

funktioniert nicht mit: durch Assoziationen.

funktioniert nicht mit: polymorphen Assoziationen.

Für Zugehörigkeitszuordnungen werden viele inverse Zuordnungen ignoriert.

Leo Le
quelle