Migration des rails pour la table de jointure has_and_belongs_to_many

Réponses:

228

Où:

class Teacher < ActiveRecord::Base
  has_and_belongs_to_many :students
end

et

class Student < ActiveRecord::Base
  has_and_belongs_to_many :teachers
end

pour les rails 4:

rails generate migration CreateJoinTableStudentTeacher student teacher

pour les rails 3:

rails generate migration students_teachers student_id:integer teacher_id:integer

pour rails <3

script/generate migration students_teachers student_id:integer teacher_id:integer

(notez que le nom de la table répertorie les deux tables de jointure par ordre alphabétique)

puis pour les rails 3 et inférieurs uniquement, vous devez modifier votre migration générée afin qu'aucun champ d'identifiant ne soit créé:

create_table :students_teachers, :id => false do |t|
dangereux
la source
16
C'est la seule réponse qui répond réellement à la question.
pingu
8
@pingu: sauf que ça ne marche pas, du moins dans Rails 3.2. Le fichier de migration généré est vide.
hoffmanc
7
Works for Rails 4.
Felipe Zavan
2
@hoffmanc Cela générera un fichier de migration vide si vous ne spécifiez aucun champ. Vous devez spécifier les champs si vous souhaitez que Rails les ajoute automatiquement au fichier de migration.
Andrew
1
bonjour, j'essaye rails generate migration CreateJoinTableTeacherStudent teacher studentau lieu de rails generate migration CreateJoinTableStudentTeacher student teacher, c'est pareil? S (tudent) doit-il avant T (eacher)?
zx1986
138

Un has_and_belongs_to_manytableau doit correspondre à ce format. Je suppose que les deux modèles à rejoindre has_and_belongs_to_manysont déjà dans la base de données: appleset 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)

Si vous utilisez le :unique => truesur l'index, vous devez (dans rails3) passer :uniq => trueà has_and_belongs_to_many.

Plus d'informations: Rails Docs

MISE À JOUR 13/12/2010 Je l'ai mis à jour pour supprimer l'id et les horodatages ... Fondamentalement MattDiPasqualeet nunopoloniasont corrects: il ne doit pas y avoir d'id et il ne doit pas y avoir d'horodatage ou les rails ne permettent pas has_and_belongs_to_manyde fonctionner.

doc what
la source
6
En fait, une table de jointure ne doit avoir que les deux colonnes de références et n'a pas de colonnes d'ID ou d'horodatage. Voici un meilleur exemple de migration has_and_belongs_to_many à partir du lien que vous avez donné. Je cherche un moyen de le faire à partir de la ligne de commande avec script/generate migration...
ma11hew28
Eh bien, il n'est pas nécessaire d'avoir les horodatages; Je l'ai marqué comme facultatif dans mon exemple. Je recommanderais cependant d'ajouter l'identifiant. Il existe des cas où l'ID ou l'horodatage peuvent être utiles. Mais je recommande fortement l'ID.
docwhat
D'accord. Dans quel cas l'ID serait-il utile?
ma11hew28
Un exemple est si la relation est suffisamment importante pour avoir une vue. Il peut également être utilisé pour accélérer l'accès aux bases de données en transmettant le relation.id au lieu de le rechercher à plusieurs reprises. Cela facilite également le dépannage de la base de données. Surtout si les identifiants des autres colonnes sont vraiment élevés. Il est plus facile de se souvenir de id: 12345 au lieu de id: 54321-id: 67890 - Mais cela étant dit, si la table devient vraiment grande, vous voudrez peut-être pouvoir économiser de l'espace en n'allouant pas un autre identifiant pour chaque relation.
docwhat
2
Je ne pense pas que l'index multicolonne soit la bonne solution pour cela. Cela fonctionnera pour les requêtes sur des pommes particulières pour trouver les oranges associées, mais pas l'inverse. Deux index à une seule colonne permettraient d'interroger efficacement les deux directions, éventuellement avec une légère perte de contrôle d'existence d'une combinaison pomme / orange particulière).
Joseph Lord
14

Vous devez nommer le tableau avec les noms de 2 modèles que vous souhaitez connecter par ordre alphabétique et mettre les deux identifiants de modèle dans le tableau. Ensuite, connectez chaque modèle les uns aux autres en créant les associations dans le modèle.

Voici un exemple:

# 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

Mais ce n'est pas très flexible et vous devriez penser à utiliser has_many: through

nunopolonie
la source
6

La réponse du haut montre un index composite qui, je ne pense pas, sera utilisé pour rechercher des pommes à partir d'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.
# 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)

J'ai trouvé la réponse sur laquelle est basée «The Doctor What» utile et la discussion aussi certainement.

Joseph Seigneur
la source
4

Dans les rails 4, vous pouvez utiliser simplement

create_join_table: table1s,: table2s

c'est tout.

Attention: vous devez offord table1, table2 avec alphanumérique.

zw963
la source
c'est une bonne solution à jour. Notez que la table de jointure n'est pas accessible en tant que modèle, mais via les relations has_and_belongs_to_many qui sont définies sur les deux tables jointes.
Sites Web taillés du
1

J'aime faire:

rails g migration CreateJoinedTable model1:references model2:references. De cette façon, j'obtiens une migration qui ressemble à ceci:

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

J'aime avoir un index sur ces colonnes car je vais souvent faire des recherches en utilisant ces colonnes.

Jwan622
la source
add_foreign_keyéchouera s'il est placé dans la même migration que celle qui a créé les tables.
Adib Saad