¿Cuándo utilizar una relación "has_many: through" en Rails?

123

Estoy tratando de entender qué has_many :throughes y cuándo usarlo (y cómo). Sin embargo, no lo entiendo. Estoy leyendo Beginning Rails 3 y probé en Google, pero no puedo entender.

LuckyLuke
fuente

Respuestas:

177

Digamos que tiene dos modelos: Usery Group.

Si desea que los usuarios pertenezcan a grupos, puede hacer algo como esto:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

¿Qué sucede si desea realizar un seguimiento de metadatos adicionales en torno a la asociación? Por ejemplo, cuando el usuario se unió al grupo, o ¿cuál es el rol del usuario en el grupo?

Aquí es donde hace que la asociación sea un objeto de primera clase:

class GroupMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

  # has attributes for date_joined and role
end

Esto introduce una nueva tabla y elimina la group_idcolumna de la tabla del usuario.

El problema con este código es que tendría que actualizar cada vez que use la clase de usuario y cambiarla:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Este tipo de código apesta, y hace que la introducción de cambios como este sea dolorosa.

has_many :through te ofrece lo mejor de ambos mundos:

class User < ActiveRecord::Base
  has_many :groups, :through => :group_memberships  # Edit :needs to be plural same as the has_many relationship   
  has_many :group_memberships
end

Ahora puede tratarlo como algo normal has_many, pero obtenga el beneficio del modelo de asociación cuando lo necesite.

Tenga en cuenta que también puede hacer esto con has_one.

Editar: facilitando la adición de un usuario a un grupo

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end
Ben Scheirman
fuente
2
Aquí es donde agregaría un método en el usermodelo para agregar el grupo. Algo así como la edición que acabo de hacer. Espero que esto ayude.
Ben Scheirman
Ya veo, ¿también tengo que escribir user.groups << group? ¿O todo lo maneja esta asociación?
LuckyLuke
Tengo una clase que es la misma que la de group_membership y tengo problemas para usar a la chica de fábrica. ¿Me ayudarías?
Ayman Salah
Yo usaría "has_many: group_memberships". Usar el singular funcionará, pero user.group_membership será una colección y user.group_memberships no funcionará.
calasyr
Eso fue un error tipográfico. Fijo.
Ben Scheirman
212

Digamos que tienes estos modelos:

Car
Engine
Piston

Un automóvil has_one :engine
Un motor belongs_to :car
Un motor has_many :pistons
Pistónbelongs_to :engine

Un coche has_many :pistons, through: :engine
pistónhas_one :car, through: :engine

Esencialmente, está delegando una relación de modelo a otro modelo, por lo que en lugar de tener que llamar car.engine.pistons, simplemente puede hacercar.pistons

cpuguy83
fuente
9
¡Esto tiene mucho sentido!
RajG
24
Yo llamo a esto SACA - ShortAndClearAnswer.
Asme Just
18

Tablas de unión de ActiveRecord

has_many :throughy las has_and_belongs_to_manyrelaciones funcionan a través de una tabla de unión , que es una tabla intermedia que representa la relación entre otras tablas. A diferencia de una consulta JOIN, los datos se almacenan en una tabla.

Diferencias prácticas

Con has_and_belongs_to_many, no necesita una clave principal y accede a los registros a través de las relaciones ActiveRecord en lugar de a través de un modelo ActiveRecord. Por lo general, utiliza HABTM cuando desea vincular dos modelos con una relación de muchos a muchos.

Utiliza una has_many :throughrelación cuando desea interactuar con la tabla de unión como un modelo Rails, completa con claves primarias y la capacidad de agregar columnas personalizadas a los datos unidos. Esto último es particularmente importante para los datos que son relevantes para las filas unidas, pero que en realidad no pertenecen a los modelos relacionados, por ejemplo, almacenar un valor calculado derivado de los campos en la fila unida.

Ver también

En A Guide to Active Record Associations , la recomendación dice:

La regla general más simple es que debe configurar una relación has_many: through si necesita trabajar con el modelo de relación como una entidad independiente. Si no necesita hacer nada con el modelo de relación, puede ser más sencillo configurar una relación has_and_belongs_to_many (aunque deberá recordar crear la tabla de unión en la base de datos).

Debe usar has_many: through si necesita validaciones, devoluciones de llamada o atributos adicionales en el modelo de combinación.

Todd A. Jacobs
fuente