Truquitos: Cómo crear asociaciones muchos-a-muchos en Rails

En Rails, hay dos maneras principales de crear relaciones muchos-a-muchos entre modelos:

  • usando una asociación has_and_belongs_to_many
  • usando una asociación has_many :through

Esto es Truquitos, así que vamos al grano.

Usando has_and_belongs_to_many

Esta es la manera más fácil de establecer una relación muchos-a-muchos. Creará una tabla de base de datos para relacionar los dos modelos (pero no un tercer modelo para representar la relación).

Supón que tienes dos modelos que necesitan tener una relación muchos-a-muchos:

  • instruments (instrumentos)
  • musicians (músicos)

El primer paso es escribir una migración, que (para este ejemplo) se vería así:

class AddHABTMForInstrumentsAndmusicians < ActiveRecord::Migration[6.0]
  def change
    create_table :instruments_musicians, id: false do |t|
      t.belongs_to :instrument
      t.belongs_to :musician
    end
  end
end

Si ejecutas esta migración generará la siguiente descripción de tabla en schema.rb:

  create_table "instruments_musicians", id: false, force: :cascade do |t|
    t.bigint "instrument_id"
    t.bigint "musician_id"
    t.index ["instrument_id"], name: "index_instruments_musicians_on_instrument_id"
    t.index ["musician_id"], name: "index_instruments_musicians_on_musician_id"
  end

Ahora lo único que necesitas es agregar la línea has_and_belongs_to_many a cada modelo, así:

class Instrument < ApplicationRecord
  has_and_belongs_to_many :musicians
end

class Musician < ApplicationRecord
  has_and_belongs_to_many :instruments
end

Y hemos terminado, eso es todo lo que necesitas para crear una relación has_and_belongs_to_many.

Usando has_many :through

La diferencia principal entre esta opción y la primera es que ahora se crea un tercer modelo. Tal modelo puede contener cosas como comportamiento personalizado o validaciones, y puede ser útil en muchos casos.

Volviendo a nuestro ejemplo, para crear una asociación has_many :through necesitamos escribir una migración que cree un modelo que lógicamente conecte músicos e instrumentos, algo como un cancionero:

    create_table :songbooks do |t|
      t.belongs_to :instrument
      t.belongs_to :musician
      t.string :title #podemos agregar atributos extra si queremos
      t.timestamps
    end

Ahora, podemos agregar las asociaciones a los modelos:

class Songbook < ApplicationRecord
    belongs_to :instrument
    belongs_to :musician
end

class Instrument < ApplicationRecord
  has_many :songbooks
  has_many :musicians, through: :songbooks
end

class Musician < ApplicationRecord
  has_many :songbooks
  has_many :instruments, through: :songbooks
end

Y así es como creas una asociación has_many :through.

¿Cuál debería usar?

Mi regla general es la siguiente: Si necesito validaciones o cualquier forma de comportamiento personalizado en la relación, voy con has_many :through. De otra manera (la mayoría de casos) voy con has_and_belongs_to_many.

¡Gracias por leer!

Qué hacer después

Juan Luis Orozco Villalobos

¡Hola! Soy Juan, ingeniero de software y consultor en Budapest. Me especializo en computación en la nube e IA, y me encanta ayudar a otros a aprender sobre tecnología e ingeniería