J'essaie de comprendre inverse_of
Et je ne comprends pas.
À quoi ressemble le SQL généré, le cas échéant?
L'option inverse_of
Présente-t-elle le même comportement si elle est utilisée avec :has_many
, :belongs_to
Et :has_many_and_belongs_to
?
Désolé si c'est une question aussi 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
D'après la documentation , il semble que l'option :inverse_of
Soit une méthode permettant d'éviter les requêtes SQL sans les générer. ActiveRecord suggère d'utiliser des données déjà chargées au lieu de les récupérer à nouveau via 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 de dungeon.traps.first.dungeon
Devrait renvoyer l'objet dungeon
d'origine au lieu d'en charger un nouvel, comme ce serait le cas par défaut.
Je pense :inverse_of
est particulièrement utile lorsque vous travaillez avec des associations qui n'ont pas encore été conservées. 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 le :inverse_of
arguments, t.project
retournera nil
, car il déclenche une requête SQL et les données ne sont pas encore stockées. Avec le :inverse_of
arguments, les données sont extraites de la mémoire.
De la documentation de Rails 5.0 et super.
Associations bidirectionnelles
Il est normal que les associations travaillent dans deux directions, nécessitant une déclaration selon deux modèles différents:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
Par défaut, Active Record ne connaît pas la connexion entre ces associations. Cela peut entraîner la désynchronisation de deux copies d'un objet:
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
Cela est dû au fait que a et b.author sont deux représentations différentes en mémoire des mêmes données et qu’aucune des deux n’est automatiquement actualisée à partir des modifications apportées à l’autre. Active Record fournit l'option: inverse_of pour que vous puissiez l'informer de ces relations:
class Author < ApplicationRecord
has_many :books, inverse_of: :author
end
class Book < ApplicationRecord
belongs_to :author, inverse_of: :books
end
Avec ces modifications, Active Record ne chargera qu'une copie de l'objet auteur, évitant les incohérences et rendant votre application plus efficace:
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
Le support de inverse_of comporte quelques limitations:
Ils ne travaillent pas avec: par le biais d'associations. Ils ne fonctionnent pas avec: les associations polymorphes. Ils ne travaillent pas avec: comme des associations.
Pour les associations d'appartements à, de nombreuses associations inverses sont ignorées. Chaque association essaiera de trouver automatiquement l'association inverse et de définir l'option: inverse_of de manière heuristique (en fonction du nom de l'association). La plupart des associations avec des noms standard seront supportées. Toutefois, les inverses des associations contenant les options suivantes ne seront pas définies automatiquement:
Juste une mise à jour pour tout le monde - nous venons d'utiliser inverse_of
avec l'une de nos applications avec un has_many :through
association
Fondamentalement, l'objet "Origin" est disponible pour l'objet "enfant"
Donc, si vous utilisez l'exemple de 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
En utilisant :inverse_of
vous permettra d'accéder à l'objet de données dont il est l'inverse, sans effectuer d'autres requêtes SQL
Lorsque nous avons deux modèles avec une relation has_many et une relation Appartient à, il est toujours préférable d'utiliser inverse_of qui informe ActiveRecod qu'ils appartiennent au même côté de l'association. Ainsi, si une requête d'un côté est déclenchée, elle se mettra en cache et servira de cache si elle est déclenchée de la direction opposée. Ce qui améliore les performances. A partir de Rails 4.1, inverse_of sera défini automatiquement, si nous utilisons foreign_key ou si le nom de la classe est modifié, nous devons le définir explicitement.
Meilleur article pour plus de détails et exemple.
http://viget.com/extend/exploring-the-inverse-of-option-on-Rails-model-associations
Jetez un coup d'oeil à cet article !!
http://gsusmonzon.blogspot.com.br/2011/09/Rails-power-of-inverseof.html
Si vous avez une relation has_many_through
Entre deux modèles, Utilisateur et Rôle, et que vous souhaitez valider l'affectation de modèle de connexion avec des entrées non existantes ou invalides avec validates_presence of :user_id, :role_id
, Cela est utile. Vous pouvez toujours générer un utilisateur @ utilisateur avec son association @user.role(params[:role_id])
afin que l'enregistrement de l'utilisateur n'entraîne pas l'échec de la validation du modèle d'affectation.