Migração de Rails para a tabela de junção has_and_belongs_to_many

122

Como faço script/generate migrationpara criar uma tabela de junção para um has_and_belongs_to_manyrelacionamento?

O aplicativo é executado no Rails 2.3.2, mas também tenho o Rails 3.0.3 instalado.

ma11hew28
fonte

Respostas:

228

Onde:

class Teacher < ActiveRecord::Base
  has_and_belongs_to_many :students
end

e

class Student < ActiveRecord::Base
  has_and_belongs_to_many :teachers
end

para trilhos 4:

rails generate migration CreateJoinTableStudentTeacher student teacher

para trilhos 3:

rails generate migration students_teachers student_id:integer teacher_id:integer

para trilhos <3

script/generate migration students_teachers student_id:integer teacher_id:integer

(observe que o nome da tabela lista as duas tabelas de junção em ordem alfabética)

e somente para os trilhos 3 e abaixo, é necessário editar a migração gerada para que um campo de ID não seja criado:

create_table :students_teachers, :id => false do |t|
dangerousdave
fonte
16
Esta é a única resposta que realmente responde à pergunta.
pingu
8
@pingu: exceto que não funciona, pelo menos no Rails 3.2. O arquivo de migração gerado está em branco.
Hoffmanc
7
Trabalha para Rails 4.
Felipe Zaavã
2
@hoffmanc Gerará um arquivo de migração vazio se você não especificar nenhum campo. Você deve especificar os campos se desejar que o Rails os adicione automaticamente ao arquivo de migração.
Andrew Andrew
1
Olá, estou tentando em rails generate migration CreateJoinTableTeacherStudent teacher studentvez de rails generate migration CreateJoinTableStudentTeacher student teacher, é o mesmo? S (tudent) precisa antes de T (eacher)?
Zx1986
138

Uma has_and_belongs_to_manytabela deve corresponder a este formato. Estou assumindo que os dois modelos aos quais se juntam has_and_belongs_to_manyjá estão no DB: applese oranges:

create_table :apples_oranges, :id => false do |t|
  t.references :apple, :null => false
  t.references :orange, :null => false
end

# Adding the index can massively speed up join tables. Don't use the
# unique if you allow duplicates.
add_index(:apples_oranges, [:apple_id, :orange_id], :unique => true)

Se você usar o :unique => trueno índice, deverá (no rails3) passar :uniq => truepara has_and_belongs_to_many.

Mais informações: Rails Docs

ATUALIZADO 13-12-2010 Atualizei-o para remover a identificação e os carimbos de data e hora ... Basicamente, MattDiPasqualee nunopoloniaestão corretos: não deve haver um ID e não deve haver carimbos de data e hora ou os trilhos não permitirão has_and_belongs_to_manyfuncionar.

docwhat
fonte
6
Na verdade, uma tabela de junção deve ter apenas as duas colunas de referência e não possuir colunas de identificação ou registro de data e hora. Aqui está um exemplo melhor de uma migração has_and_belongs_to_many a partir do link que você forneceu. Estou procurando uma maneira de fazê-lo a partir da linha de comando com script/generate migration...
ma11hew28
Bem, ele não precisa ter os carimbos de data e hora; Eu o marquei opcional no meu exemplo. Eu recomendaria adicionar o ID, no entanto. Há casos em que o ID ou o carimbo de data e hora podem ser úteis. Mas eu recomendo fortemente o ID.
docwhat
Está bem. Qual é o caso em que o ID seria útil?
ma11hew28
Um exemplo é se o relacionamento é importante o suficiente para ter uma visão. Também pode ser usado para acelerar o acesso às bases de dados, passando o relacionamento.id em vez de procurá-lo repetidamente. Também facilita a solução de problemas do banco de dados. Especialmente se os IDs das outras colunas forem realmente altos. É mais fácil lembrar o id: 12345 em vez do id: 54321-id: 67890 - Mas, dito isso, se a tabela ficar muito grande, convém economizar espaço, não alocando outro ID para cada relacionamento.
docwhat
2
Não acho que o índice de várias colunas seja a solução certa para isso. Ele funcionará para consultas de maçãs específicas para encontrar as laranjas relacionadas, mas não o contrário. Dois índices de coluna única permitiriam que ambas as direções fossem consultadas com eficiência, possivelmente com uma pequena perda de verificações de existência de uma combinação específica de maçã e laranja).
Joseph Lord
14

Você deve nomear na tabela os nomes dos 2 modelos que deseja conectar por ordem alfabética e colocar os dois IDs de modelo na tabela. Em seguida, conecte cada modelo um ao outro, criando as associações no modelo.

Aqui está um exemplo:

# in migration
def self.up
  create_table 'categories_products', :id => false do |t|
    t.column :category_id, :integer
    t.column :product_id, :integer
  end
end

# models/product.rb
has_and_belongs_to_many :categories

# models/category.rb
has_and_belongs_to_many :products

Mas isso não é muito flexível e você deve pensar em usar has_many: através

nunopolonia
fonte
6

A resposta superior mostra um índice composto que não acredito que será usado para procurar maçãs de laranjas.

create_table :apples_oranges, :id => false do |t|
  t.references :apple, :null => false
  t.references :orange, :null => false
end

# Adding the index can massively speed up join tables.
# This enforces uniqueness and speeds up apple->oranges lookups.
add_index(:apples_oranges, [:apple_id, :orange_id], :unique => true)
# This speeds up orange->apple lookups
add_index(:apples_oranges, :orange_id)

Eu encontrei a resposta em que isso é baseado em 'The Doctor What' útil e a discussão certamente também.

Joseph Lord
fonte
4

Nos trilhos 4, você pode usar de maneira simples

create_join_table: table1s,: table2s

isso é tudo.

Cuidado: você deve oferecer tabela1, tabela2 com alfanumérico.

zw963
fonte
Esta é uma boa solução atualizada. Observe que a tabela de junção não está acessível como modelo, mas através das relações has_and_belongs_to_many configuradas nas duas tabelas unidas.
Sites Taylored
1

Eu gosto de fazer:

rails g migration CreateJoinedTable model1:references model2:references. Dessa forma, recebo uma migração parecida com esta:

class CreateJoinedTable < ActiveRecord::Migration
  def change
    create_table :joined_tables do |t|
      t.references :trip, index: true
      t.references :category, index: true
    end
    add_foreign_key :joined_tables, :trips
    add_foreign_key :joined_tables, :categories
  end
end

Eu gosto de ter um índice nessas colunas, porque frequentemente faço pesquisas usando essas colunas.

Jwan622
fonte
add_foreign_keyfalhará se colocado na mesma migração que a que criou as tabelas.
Adib Saad