¿Qué hace inverse_of? ¿Qué SQL genera?


143

Estoy tratando de entender inverse_ofy no lo entiendo.

¿Qué aspecto tiene el sql generado, si lo hay?

¿Tiene la inverse_ofopción de mostrar el mismo comportamiento si se utiliza con :has_many, :belongs_toy :has_many_and_belongs_to?

Lo siento si esta es una pregunta tan básica.

Vi este ejemplo:

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

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

Respuestas:


125

De la documentación , parece que la :inverse_ofopción es un método para evitar consultas SQL, no generarlas. Es una pista para que ActiveRecord use datos ya cargados en lugar de recuperarlos nuevamente a través de una relación.

Su ejemplo:

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

En este caso, la llamada dungeon.traps.first.dungeondebería devolver el dungeonobjeto original en lugar de cargar uno nuevo, como sería el caso por defecto.


55
¿Entiende el comentario en la documentación: "para las asociaciones pertenece a las asociaciones has_many se ignoran muchas asociaciones inversas". Y sin embargo, el documento utiliza ese ejemplo exacto. ¿Que me estoy perdiendo aqui?
dynex

50
Todo esto es muy extraño para mí, porque me parece que siempre querrías este comportamiento por defecto, y solo necesitas usar: inverse_of cuando no se puede inferir el nombre de la asociación. También las inconsistencias en la definición son molestas, pero me han ayudado en algunos casos. ¿Alguna razón por la que no debería pegarlo en todas partes?
Ibrahim

18
@Ibrahim ¡Mira esto, se fusionó hace 23 días! github.com/rails/rails/pull/9522
Hut8

66
Tiene sentido que se ignore el inverso de una asociación belong_to porque no se garantiza que el hijo del registro principal de un registro A sea el registro A; sin embargo, podría ser un hermano del Registro A. El padre de un hijo del registro A, sin embargo , se garantiza que será el récord A.
David Aldridge

2
El futuro lector podría obtener ayuda de este blog ...: D
Arup Rakshit

42

Creo que :inverse_ofes más útil cuando trabajas con asociaciones que aún no se han conservado. P.ej:

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

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

Ahora, en la consola:

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

Sin los :inverse_ofargumentos, t.projectvolvería nil, porque desencadena una consulta sql y los datos aún no se almacenan. Con los :inverse_ofargumentos, los datos se recuperan de la memoria.


1
Tuve un problema con accept_nested_attributes_for. Por defecto, solo se muestran los atributos anidados para los objetos asociados existentes (acción de edición). Si, por ejemplo, desea CREAR un objeto con, digamos, 3 objetos asociados, debe tener Model.new (nueva acción) y: inverse_of en sus modelos.
Victor Marconi

Convino en el comportamiento en Rails 4 y posteriores, pero funcionó bien en v3 (excepto algunas encarnaciones posteriores, aunque la sintaxis anterior funciona nuevamente en v3.2.13). Y tenga en cuenta que en el modelo de unión, ya no se puede validar la presencia de la identificación, solo el objeto-modelo. Parece que puede tener una asociación sin una identificación, en v4 'lógica'.
JosephK

Exactamente ... :inverse_ofresolvió un problema para mí al crear nuevas entidades principales y secundarias en la misma forma.
WM

16

Después de este pr ( https://github.com/rails/rails/pull/9522 ) no se requiere inverse_of en la mayoría de los casos.

Active Record admite la identificación automática para la mayoría de las asociaciones con nombres estándar. Sin embargo, Active Record no identificará automáticamente las asociaciones bidireccionales que contienen un alcance o cualquiera de las siguientes opciones:

  • :mediante
  • :clave externa
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

En el ejemplo anterior, una referencia al mismo objeto se almacena en la variable ay en el atributo writer.


Estoy usando Rails 5, y ya sea que agregues inverse_ofo no, el resultado a.first_name == b.author.first_namees siempre seguro.
Arslan Ali

@ArslanAli gracias por el gran comentario, actualicé la respuesta.
artamonovdev

5

Solo una actualización para todos: acabamos de usar inverse_ofuna de nuestras aplicaciones con una has_many :throughasociación


Básicamente hace que el objeto "origen" esté disponible para el objeto "hijo"

Entonces, si estás usando el ejemplo 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

El uso :inverse_ofle permitirá acceder al objeto de datos del que es el inverso, sin realizar más consultas SQL


5

Cuando tenemos 2 modelos con la relación has_many y belong_to, siempre es mejor usar inverse_of que informa a ActiveRecod de que pertenecen al mismo lado de la asociación. Entonces, si se activa una consulta desde un lado, se almacenará en caché y se servirá desde el caché si se activa desde la dirección opuesta. Lo que mejora en el rendimiento. A partir de Rails 4.1, inverse_of se establecerá automáticamente, si usamos Foreign_key o cambios en el nombre de la clase, debemos establecerlo explícitamente.

El mejor artículo para detalles y ejemplo.

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



3

Si tiene una has_many_throughrelación entre dos modelos, Usuario y Rol, y desea validar la Asignación del modelo de conexión con entradas no válidas o no existentes validates_presence of :user_id, :role_id, es útil. Todavía puede generar un Usuario @usuario con su asociación @user.role(params[:role_id])para que guardar el usuario no resulte en una validación fallida del modelo de Asignación.


-1

Por favor, eche un vistazo 2 dos recursos útiles

Y recuerda algunas limitaciones de inverse_of:

no funciona con: a través de asociaciones.

no funciona con: asociaciones polimórficas.

para las asociaciones pertenece a las asociaciones has_many se ignoran las asociaciones inversas

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.