Rails-Migration für die Join-Tabelle has_and_belongs_to_many

122

Wie script/generate migrationerstelle ich eine Verknüpfungstabelle für eine has_and_belongs_to_manyBeziehung?

Die Anwendung läuft auf Rails 2.3.2, aber ich habe auch Rails 3.0.3 installiert.

ma11hew28
quelle

Antworten:

228

Wo:

class Teacher < ActiveRecord::Base
  has_and_belongs_to_many :students
end

und

class Student < ActiveRecord::Base
  has_and_belongs_to_many :teachers
end

für Schienen 4:

rails generate migration CreateJoinTableStudentTeacher student teacher

für Schienen 3:

rails generate migration students_teachers student_id:integer teacher_id:integer

für Schienen <3

script/generate migration students_teachers student_id:integer teacher_id:integer

(Beachten Sie, dass der Tabellenname beide Join-Tabellen in alphabetischer Reihenfolge auflistet.)

und dann müssen Sie nur für Schienen 3 und darunter Ihre generierte Migration bearbeiten, damit kein ID-Feld erstellt wird:

create_table :students_teachers, :id => false do |t|
gefährliche Taube
quelle
16
Dies ist die einzige Antwort, die die Frage tatsächlich beantwortet.
Pingu
8
@pingu: außer dass es nicht funktioniert, zumindest in Rails 3.2. Die generierte Migrationsdatei ist leer.
Hoffmanc
7
Works for Rails 4.
Felipe Zavan
2
@hoffmanc Es wird eine leere Migrationsdatei generiert, wenn Sie keine Felder angeben. Sie müssen die Felder angeben, wenn Rails sie automatisch zur Migrationsdatei hinzufügen soll.
Andrew
1
Hallo, ich versuche rails generate migration CreateJoinTableTeacherStudent teacher studentstattdessen rails generate migration CreateJoinTableStudentTeacher student teacher, ist das das gleiche? Muss S (tudent) vor T (jeweils) stehen?
zx1986
138

Eine has_and_belongs_to_manyTabelle muss diesem Format entsprechen. Ich gehe davon aus, dass die beiden zu verbindenden Modelle has_and_belongs_to_manybereits in der Datenbank enthalten sind: applesund 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)

Wenn Sie das :unique => truefür den Index verwenden, sollten Sie (in Rails3) :uniq => truean übergeben has_and_belongs_to_many.

Weitere Informationen: Rails Docs

AKTUALISIERT 13.12.2010 Ich habe es aktualisiert, um die ID und die Zeitstempel zu entfernen ... Grundsätzlich MattDiPasqualeund nunopoloniakorrekt: Es darf keine ID geben und es dürfen keine Zeitstempel oder Schienen vorhanden sein, die nicht has_and_belongs_to_manyfunktionieren.

docwhat
quelle
6
Tatsächlich sollte eine Join-Tabelle nur die beiden Referenzspalten enthalten und keine ID- oder Zeitstempelspalten. Hier ist ein besseres Beispiel für eine Migration von has_and_belongs_to_many von dem von Ihnen angegebenen Link. Ich suche nach einer Möglichkeit, dies über die Befehlszeile mit script/generate migration...
ma11hew28
Nun, es muss nicht die Zeitstempel haben; Ich habe es in meinem Beispiel als optional markiert. Ich würde jedoch empfehlen, die ID hinzuzufügen. Es gibt Fälle, in denen entweder die ID oder der Zeitstempel nützlich sein können. Aber ich empfehle dringend die ID.
docwhat
OK. Was ist ein Fall, in dem die ID nützlich wäre?
ma11hew28
Ein Beispiel ist, wenn die Beziehung wichtig genug ist, um eine Ansicht zu haben. Es kann auch verwendet werden, um den Zugriff auf die Datenbanken zu beschleunigen, indem die Beziehung.id weitergegeben wird, anstatt sie wiederholt nachzuschlagen. Dies erleichtert auch die Fehlerbehebung in der Datenbank. Besonders wenn die IDs der anderen Spalten sehr hoch sind. Es ist einfacher, sich die ID: 12345 anstelle der ID: 54321-ID: 67890 zu merken. Wenn die Tabelle jedoch sehr groß wird, möchten Sie möglicherweise Platz sparen, indem Sie nicht jeder Beziehung eine andere ID zuweisen.
docwhat
2
Ich denke nicht, dass der mehrspaltige Index die richtige Lösung dafür ist. Es funktioniert bei Abfragen für bestimmte Äpfel, um die verwandten Orangen zu finden, aber nicht umgekehrt. Zwei einspaltige Indizes würden es ermöglichen, beide Richtungen effizient abzufragen, möglicherweise mit einem geringen Verlust an Existenzprüfungen einer bestimmten Apfel-Orangen-Kombination.
Joseph Lord
14

Sie sollten der Tabelle die Namen von 2 Modellen benennen, die Sie in alphabetischer Reihenfolge verbinden möchten, und die beiden Modell-IDs in die Tabelle einfügen. Verbinden Sie dann jedes Modell miteinander, um die Assoziationen im Modell zu erstellen.

Hier ist ein Beispiel:

# 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

Dies ist jedoch nicht sehr flexibel und Sie sollten über die Verwendung von has_many: through nachdenken

Nunopolonia
quelle
6

Die obere Antwort zeigt einen zusammengesetzten Index, von dem ich nicht glaube, dass er zum Nachschlagen von Äpfeln aus Orangen verwendet wird.

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)

Ich fand die Antwort, auf der 'The Doctor What' basiert, nützlich und die Diskussion sicherlich auch.

Joseph Lord
quelle
4

In Schienen 4 können Sie einfach verwenden

create_join_table: table1s ,: table2s

das ist alles.

Achtung: Sie müssen Tabelle1, Tabelle2 mit alphanumerischen Zeichen versehen.

zw963
quelle
Dies ist eine gute, aktuelle Lösung. Beachten Sie, dass auf die Verknüpfungstabelle nicht als Modell zugegriffen werden kann, sondern über die Beziehungen has_and_belongs_to_many, die für beide verknüpften Tabellen eingerichtet sind.
Taylored Websites
1

Ich mache gerne:

rails g migration CreateJoinedTable model1:references model2:references. Auf diese Weise erhalte ich eine Migration, die folgendermaßen aussieht:

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

Ich mag es, Index für diese Spalten zu haben, weil ich oft nach diesen Spalten suche.

Jwan622
quelle
add_foreign_keyschlägt fehl, wenn es sich in derselben Migration befindet wie die, in der die Tabellen erstellt wurden.
Adib Saad