Que fait inverse_of? Quel SQL génère-t-il?

143

J'essaye de comprendre inverse_ofet je ne comprends pas.

À quoi ressemble le SQL généré, le cas échéant?

Est -ce que l' inverse_ofoption de le même comportement si elle est utilisée avec :has_many, :belongs_toet :has_many_and_belongs_to?

Désolé si c'est une question si fondamentale.

J'ai vu cet exemple:

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

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

Réponses:

125

D'après la documentation , il semble que l' :inverse_ofoption soit une méthode pour éviter les requêtes SQL, pas pour les générer. C'est un conseil à ActiveRecord d'utiliser des données déjà chargées au lieu de les récupérer à travers une relation.

Leur exemple:

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

Dans ce cas, l'appel dungeon.traps.first.dungeondoit retourner l' dungeonobjet d' origine au lieu d'en charger un nouveau comme ce serait le cas par défaut.

tadman
la source
5
Comprenez-vous le commentaire de la documentation: "pour les associations appartiennent_à de nombreuses associations inverses sont ignorées.". Et pourtant, le doc utilise cet exemple exact. Qu'est-ce que j'oublie ici?
dynex
50
Tout cela m'est très étrange, car il me semble que vous voudriez toujours ce comportement par défaut, et que vous n'avez besoin d'utiliser: inverse_of que lorsque le nom de l'association ne peut pas être déduit. Les incohérences dans la définition sont également gênantes, mais cela m'a aidé dans quelques cas. Une raison pour laquelle je ne devrais pas le coller partout?
Ibrahim
18
@Ibrahim Vérifiez ceci, il a été fusionné il y a 23 jours! github.com/rails/rails/pull/9522
Hut8
6
Il est logique que l'inverse d'une association appartient_to soit ignoré car l'enfant de l'enregistrement parent d'un enregistrement A n'est pas garanti comme étant l'enregistrement A - il peut s'agir d'un frère de l'enregistrement A. Le parent d'un enfant de l'enregistrement A, cependant , est garanti d'être record A.
David Aldridge
2
Le futur lecteur pourrait obtenir de l'aide de ce blog ...: D
Arup Rakshit
42

Je pense que :inverse_ofc'est plus utile lorsque vous travaillez avec des associations qui n'ont pas encore été maintenues. Par exemple:

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

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

Maintenant, dans la 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, ...>

Sans les :inverse_ofarguments, t.projectretournerait nil, car il déclenche une requête SQL et les données ne sont pas encore stockées. Avec les :inverse_ofarguments, les données sont extraites de la mémoire.

KenB
la source
1
J'ai eu un problème avec accepte_nested_attributes_for. Par défaut, seuls les attributs imbriqués pour les objets associés existants s'affichent (action d'édition). Si, par exemple, vous souhaitez CRÉER un objet avec, par exemple, 3 objets associés, vous devez avoir Model.new (nouvelle action) et: inverse_of dans vos modèles.
Victor Marconi
D'accord sur le comportement dans Rails 4 et versions ultérieures, mais cela fonctionnait très bien dans la v3 (sauf certaines incarnations ultérieures, bien que l'ancienne syntaxe fonctionne à nouveau dans la v3.2.13). Et notez que dans le modèle de jointure, ne peut plus valider la présence des identifiants - seulement l'objet-modèle. Il semble que vous pouvez avoir une association sans identifiant, dans la «logique» v4.
JosephK
Exactement .. :inverse_ofrésolu un problème pour moi lors de la création de nouvelles entités parent et enfant sous la même forme.
WM
16

Après ce pr ( https://github.com/rails/rails/pull/9522 ) inverse_of n'est pas nécessaire dans la plupart des cas.

Active Record prend en charge l'identification automatique pour la plupart des associations avec des noms standard. Cependant, Active Record n'identifiera pas automatiquement les associations bidirectionnelles contenant une étendue ou l'une des options suivantes:

  • :à travers
  • :clé étrangère
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

Dans l'exemple ci-dessus, une référence au même objet est stockée dans la variable aet dans l'attribut writer.

artamonovdev
la source
J'utilise Rails 5, et que vous ajoutez inverse_ofou non, le résultat pour a.first_name == b.author.first_nameest toujours ture.
Arslan Ali
@ArslanAli merci pour l'excellent commentaire, j'ai mis à jour la réponse.
artamonovdev
5

Juste une mise à jour pour tout le monde - nous venons de l'utiliser inverse_ofavec l'une de nos applications avec une has_many :throughassociation


Il met essentiellement l’objet «origine» à la disposition de l’objet «enfant»

Donc, si vous utilisez l'exemple des 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'utilisation :inverse_ofvous permettra d'accéder à l'objet de données dont il est l'inverse, sans effectuer d'autres requêtes SQL

Richard Peck
la source
5

Lorsque nous avons 2 modèles avec une relation has_many et appartient_to, il est toujours préférable d'utiliser inverse_of qui informe ActiveRecod qu'ils appartiennent du même côté de l'association. Ainsi, si une requête d'un côté est déclenchée, elle sera mise en cache et servira à partir du cache si elle est déclenchée dans la direction opposée. Ce qui améliore les performances. À partir de Rails 4.1, inverse_of sera défini automatiquement, si nous utilisons une clé étrangère ou des changements de nom de classe, nous devons définir explicitement.

Meilleur article pour les détails et l'exemple.

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

Gurudath BN
la source
3

Si vous avez une has_many_throughrelation entre deux modèles, Utilisateur et Rôle, et que vous souhaitez valider l'affectation du modèle de connexion par rapport à des entrées non existantes ou non valides avec validates_presence of :user_id, :role_id, c'est utile. Vous pouvez toujours générer un utilisateur @user avec son association @user.role(params[:role_id])afin que l'enregistrement de l'utilisateur n'entraîne pas un échec de validation du modèle d'affectation.

Informatom
la source
-1

Veuillez jeter un oeil 2 deux ressources utiles

Et rappelez-vous certaines limites de inverse_of:

ne fonctionne pas avec: par les associations.

ne fonctionne pas avec: les associations polymorphes.

pour les associations appartient_to, de nombreuses associations inverses sont ignorées.

Léo Le
la source