Cosa fa inverse_of? Quale SQL genera?


143

Sto cercando di farmi girare la testa inverse_ofe non capisco.

Che aspetto ha il sql generato, se presente?

Ha l' inverse_ofopzione di presentare lo stesso comportamento se utilizzato con :has_many, :belongs_toe :has_many_and_belongs_to?

Scusate se questa è una domanda così basilare.

Ho visto questo esempio:

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

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

Risposte:


125

Dalla documentazione , sembra che il:inverse_of opzione sia un metodo per evitare query SQL, non generarle. È un suggerimento per ActiveRecord di utilizzare i dati già caricati invece di recuperarli nuovamente attraverso una relazione.

Il loro esempio:

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 questo caso, la chiamata dungeon.traps.first.dungeondovrebbe restituire l' dungeonoggetto originale invece di caricarne uno nuovo come per impostazione predefinita.


5
Comprendi il commento nella documentazione: "per le associazioni appartiene a molte associazioni inverse vengono ignorate". Eppure il documento usa quell'esatto esempio. Cosa mi sto perdendo qui?
dynex,

50
Questo è tutto molto strano per me, perché mi sembra che vorresti sempre questo comportamento di default, e devi solo usare: inverse_of quando il nome dell'associazione non può essere dedotto. Anche le incoerenze nella definizione sono fastidiose, ma in alcuni casi mi ha aiutato. Qualche motivo per cui non dovrei semplicemente attaccarlo ovunque?
Ibrahim,

18
@Ibrahim Dai un'occhiata, è stato unito 23 giorni fa! github.com/rails/rails/pull/9522
Hut8

6
Ha senso che l'inverso di un'associazione appartiene a un'associazione venga ignorato perché il figlio di un record del genitore A non è garantito essere il record A - potrebbe essere un fratello del record A. Il genitore di un figlio del record A, tuttavia , è garantito per essere il record A.
David Aldridge il

2
Il futuro lettore potrebbe ricevere aiuto da questo blog ...: D
Arup Rakshit,

42

Penso che :inverse_ofsia molto utile quando lavori con associazioni che non sono ancora state mantenute. Per esempio:

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

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

Ora, nella console:

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

Senza gli :inverse_ofargomenti, t.projectritornerebbe nil, perché innesca una query sql e i dati non sono ancora memorizzati. Con gli :inverse_ofargomenti, i dati vengono recuperati dalla memoria.


1
Ho avuto un problema con accept_nested_attributes_for. Per impostazione predefinita, vengono visualizzati solo gli attributi nidificati per gli oggetti associati esistenti (modifica azione). Se, ad esempio, desideri CREARE un oggetto con, per esempio, 3 oggetti associati, dovresti avere Model.new (nuova azione) e: inverse_of nei tuoi modelli.
Victor Marconi,

Concordato sul comportamento in Rails 4 e versioni successive, ma ha funzionato perfettamente in v3 (tranne alcune incarnazioni successive, sebbene la vecchia sintassi funzioni di nuovo in v3.2.13). E nota nel modello di join, non è più possibile convalidare la presenza dell'id - solo l'oggetto del modello. Sembra che tu possa avere un'associazione senza un id per essa, in "logica" v4.
JosephK,

Esatto ... :inverse_ofho risolto un problema per me quando creavo nuove entità padre e figlio nella stessa forma.
WM,

16

Dopo questo pr ( https://github.com/rails/rails/pull/9522 ) non è richiesto nella maggior parte dei casi inverse_of .

Active Record supporta l'identificazione automatica per la maggior parte delle associazioni con nomi standard. Tuttavia, Active Record non identificherà automaticamente le associazioni bidirezionali che contengono un ambito o una delle seguenti opzioni:

  • :attraverso
  • : foreign_key
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

Nell'esempio sopra, un riferimento allo stesso oggetto è memorizzato nella variabile ae nell'attributo writer.


Sto usando Rails 5, e se lo aggiungi inverse_ofo no, il risultato a.first_name == b.author.first_nameè sempre perfetto.
Arslan Ali,

@ArslanAli grazie per l'ottimo commento, ho aggiornato la risposta.
artamonovdev,

5

Solo un aggiornamento per tutti: abbiamo appena usato inverse_ofuna delle nostre app con has_many :throughun'associazione


Fondamentalmente rende l'oggetto "origine" disponibile per l'oggetto "figlio"

Quindi se stai usando l'esempio di 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

L'utilizzo :inverse_ofconsentirà di accedere all'oggetto dati di cui è l'inverso, senza eseguire ulteriori query SQL


5

Quando abbiamo 2 modelli con has_many e appartiene_a relazione, è sempre meglio usare inverse_of che informa ActiveRecod che appartengono allo stesso lato dell'associazione. Pertanto, se viene attivata una query da un lato, verrà memorizzata nella cache e verrà pubblicata dalla cache se viene attivata dalla direzione opposta. Il che migliora le prestazioni. Da Rails 4.1, inverse_of verrà impostato automaticamente, se utilizziamo foreign_key o cambi nel nome della classe, dobbiamo impostare esplicitamente.

Il miglior articolo per dettagli ed esempi.

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



3

Se si dispone di una has_many_throughrelazione tra due modelli, Utente e Ruolo e si desidera convalidare l'assegnazione del modello di connessione rispetto a voci inesistenti o non valide con validates_presence of :user_id, :role_id, è utile. È comunque possibile generare un utente @utente con la sua associazione in @user.role(params[:role_id])modo che il salvataggio dell'utente non comporti una mancata convalida del modello di assegnazione.


-1

Si prega di dare un'occhiata 2 due risorse utili

E ricorda alcune limitazioni di inverse_of:

non funziona con: attraverso le associazioni.

non funziona con: associazioni polimorfiche.

per appartiene_a associazioni has_molte associazioni inverse vengono ignorate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.