Rails has_and_belongs_to_many migración

119

Tengo dos modelos restauranty userquiero realizar una relación has_and_belongs_to_many.

Ya he entrado en los archivos del modelo y he añadido el has_and_belongs_to_many :restaurantsyhas_and_belongs_to_many :users

Supongo que en este punto debería poder hacer algo como con Rails 3:

rails generate migration ....

pero todo lo que he intentado parece fallar. Estoy seguro de que esto es algo realmente simple, soy nuevo en rieles, así que todavía estoy aprendiendo.

dbslone
fuente

Respuestas:

260

Debe agregar una tabla de combinación separada con solo un restaurant_idy user_id(sin clave principal), en orden alfabético .

Primero ejecute sus migraciones, luego edite el archivo de migración generado.

Carriles 3

rails g migration create_restaurants_users_table

Carriles 4 :

rails g migration create_restaurants_users

Carriles 5

rails g migration CreateJoinTableRestaurantUser restaurants users

De los documentos :

También hay un generador que producirá tablas de unión si JoinTable es parte del nombre:


Su archivo de migración (tenga en cuenta :id => false; es lo que impide la creación de una clave principal):

Carriles 3

class CreateRestaurantsUsers < ActiveRecord::Migration
  def self.up
    create_table :restaurants_users, :id => false do |t|
        t.references :restaurant
        t.references :user
    end
    add_index :restaurants_users, [:restaurant_id, :user_id]
    add_index :restaurants_users, :user_id
  end

  def self.down
    drop_table :restaurants_users
  end
end

Carriles 4

class CreateRestaurantsUsers < ActiveRecord::Migration
  def change
    create_table :restaurants_users, id: false do |t|
      t.belongs_to :restaurant
      t.belongs_to :user
    end
  end
end

t.belongs_tocreará automáticamente los índices necesarios. def changedetectará automáticamente una migración hacia adelante o hacia atrás, sin necesidad de subir / bajar.

Carriles 5

create_join_table :restaurants, :users do |t|
  t.index [:restaurant_id, :user_id]
end

Nota: También hay una opción para un nombre de tabla personalizado que se puede pasar como un parámetro para llamar a create_join_table table_name. De los documentos

De forma predeterminada, el nombre de la tabla de combinación proviene de la unión de los dos primeros argumentos proporcionados para create_join_table, en orden alfabético. Para personalizar el nombre de la tabla, proporcione una opción: table_name:

Dex
fuente
8
@Dex: solo por curiosidad, ¿podría explicar por qué está utilizando un segundo índice compuesto, definido con el orden inverso de las columnas? Tenía la impresión de que el orden de las columnas no importaba. No soy un DBA, solo quiero ampliar mi propia comprensión. ¡Gracias!
plainjimbo
11
@Jimbo No lo necesitas de esa manera, realmente depende de tus consultas. Los índices se leen de izquierda a derecha, por lo que el primero será más rápido si está buscando restaurant_id. El segundo te ayudará si estás buscando user_id. Si está buscando en ambos, creo que la base de datos sería lo suficientemente inteligente como para necesitar solo una. Así que supongo que el segundo realmente no necesita ser compuesto. Esto fue más como un ejemplo. Sin embargo, esta era una pregunta de Rails, por lo que la publicación en la sección DB arrojaría una respuesta más completa.
Dex
3
El segundo índice tiene cierta redundancia: siempre que consulte tanto restaurant_id como user_id, su orden en su SQL no importa y se utilizará el primer índice. También se utilizará si solo está consultando en restaurant_id. El segundo índice solo necesita estar en: user_id, y se usaría en los casos en que solo esté consultando sobre user_id (con lo que el primer índice no ayudaría debido al orden de sus claves).
Yardboy
13
En los carriles 4 la migración debe ser rails g migration create_restaurants_userssin mesa al final.
Fa11enAngel
6
También puede utilizar rails g migration CreateJoinTableRestaurantUser usuario de restaurante. Lea guides.rubyonrails.org/migrations.html#creating-a-join-table
eKek0
36

Las respuestas aquí están bastante anticuadas. A partir de Rails 4.0.2, sus migraciones utilizan create_join_table.

Para crear la migración, ejecute:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Esto generará lo siguiente:

class CreateJoinTableRestaurantsUsers < ActiveRecord::Migration
  def change
    create_join_table :restaurants, :users do |t|
      # t.index [:restaurant_id, :user_id]
      # t.index [:user_id, :restaurant_id]
    end
  end
end

Si desea indexar estas columnas, descomente las líneas respectivas y ¡listo!

Jan Klimo
fuente
2
Por lo general, descomentaría una de las líneas de índice y la agregaría unique: true. Esto evitará que se creen relaciones duplicadas.
Toby 1 Kenobi
26

Al crear la tabla de combinación, preste especial atención al requisito de que las dos tablas se enumeren en orden alfabético en el nombre / clase de migración. Esto puede morderlo fácilmente si los nombres de sus modelos son similares, por ejemplo, "abc" y "abb". Si fueras a correr

rails g migration create_abc_abb_table

Tus relaciones no funcionarán como se esperaba. Debes usar

rails g migration create_abb_abc_table

en lugar.

coctelera
fuente
1
¿Qué viene primero? foo o foo_bar?
B Siete
1
Se ejecutó en una consola de rieles: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] La clasificación Unix aparece igual.
Shadoath
6

Para las relaciones HABTM, debe crear una tabla de combinación. Solo hay una tabla de unión y esa tabla no debe tener una columna de identificación. Prueba esta migración.

def self.up
  create_table :restaurants_users, :id => false do |t|
    t.integer :restaurant_id
    t.integer :user_id
  end
end

def self.down
  drop_table :restaurants_users
end

Debe consultar los tutoriales de esta guía de rieles de relación

Mohit Jain
fuente
No creo que necesite un modelo y no veo nada en el enlace sobre la necesidad de un modelo para una relación HABTM.
B Siete
1
Para acelerar las consultas generadas, agregue índices a los campos de identificación.
Martin Röbert