Rails has_and_belongs_to_many migration


120

ฉันมีสองโมเดลrestaurantและuserฉันต้องการแสดงความสัมพันธ์ has_and_belongs_to_many

ฉันได้เข้าไปในไฟล์โมเดลแล้วและเพิ่มhas_and_belongs_to_many :restaurantsและhas_and_belongs_to_many :users

ฉันคิดว่า ณ จุดนี้ฉันควรจะทำอะไรบางอย่างเช่นกับ Rails 3:

rails generate migration ....

แต่ทุกสิ่งที่ฉันพยายามดูเหมือนจะล้มเหลว ฉันแน่ใจว่านี่เป็นอะไรที่ง่ายมากฉันยังใหม่กับรางดังนั้นฉันจึงยังคงเรียนรู้

คำตอบ:


260

คุณจำเป็นต้องเพิ่มตารางเข้าร่วมการแยกจากกันพร้อมเพียงrestaurant_idและuser_id(ไม่มีคีย์หลัก) ในลำดับตัวอักษร

ก่อนอื่นให้เรียกใช้การย้ายข้อมูลจากนั้นแก้ไขไฟล์การย้ายข้อมูลที่สร้างขึ้น

ราง 3

rails g migration create_restaurants_users_table

ราง 4 :

rails g migration create_restaurants_users

ราง 5

rails g migration CreateJoinTableRestaurantUser restaurants users

จากเอกสาร :

นอกจากนี้ยังมีเครื่องกำเนิดไฟฟ้าซึ่งจะสร้างตารางการเข้าร่วมหาก JoinTable เป็นส่วนหนึ่งของชื่อ:


ไฟล์การย้ายข้อมูลของคุณ (โปรดสังเกตสิ่ง:id => falseนี้คือสิ่งที่ป้องกันการสร้างคีย์หลัก):

ราง 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

ราง 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_toจะสร้างดัชนีที่จำเป็นโดยอัตโนมัติ def changeจะตรวจจับการย้ายไปข้างหน้าหรือย้อนกลับโดยอัตโนมัติโดยไม่จำเป็นต้องขึ้น / ลง

ราง 5

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

หมายเหตุ: นอกจากนี้ยังมีตัวเลือกสำหรับชื่อตารางกำหนดเองที่สามารถส่งผ่านเป็นพารามิเตอร์เพื่อ create_join_table table_nameเรียกว่า จากเอกสาร

ตามค่าเริ่มต้นชื่อของตารางการรวมมาจากการรวมกันของอาร์กิวเมนต์สองตัวแรกที่จัดเตรียมให้กับ create_join_table ตามลำดับตัวอักษร ในการกำหนดชื่อตารางเองให้ระบุตัวเลือก: table_name:


8
@Dex - ด้วยความอยากรู้คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงใช้ดัชนีสารประกอบที่สองซึ่งกำหนดด้วยลำดับคอลัมน์ที่กลับรายการ ฉันรู้สึกว่าลำดับคอลัมน์ไม่สำคัญ ฉันไม่ใช่ DBA เพียงแค่ต้องการเพิ่มพูนความเข้าใจของตัวเอง ขอบคุณ!
plainjimbo

11
@ Jimbo คุณไม่จำเป็นต้องใช้แบบนั้นมันขึ้นอยู่กับคำถามของคุณจริงๆ restaurant_idดัชนีอ่านจากซ้ายไปขวาเพื่อให้คนแรกที่จะเร็วที่สุดถ้าคุณกำลังค้นหาใน user_idที่สองจะช่วยถ้าคุณกำลังค้นหาใน หากคุณกำลังค้นหาทั้งสองอย่างฉันคิดว่าฐานข้อมูลจะฉลาดพอที่จะต้องการเพียงอย่างเดียว ดังนั้นฉันเดาว่าอันที่สองไม่จำเป็นต้องรวมกันจริงๆ นี่เป็นเพียงตัวอย่างมากกว่า นี่เป็นคำถามเกี่ยวกับ Rails ดังนั้นการโพสต์ในส่วน DB จะให้คำตอบที่สมบูรณ์ยิ่งขึ้น
Dex

3
ดัชนีที่สองมีความซ้ำซ้อน - ตราบใดที่คุณกำลังค้นหาทั้ง restaurant_id และ user_id ลำดับใน SQL ของคุณก็ไม่สำคัญและจะใช้ดัชนีแรก นอกจากนี้ยังจะใช้หากคุณกำลังค้นหาเฉพาะ restaurant_id ดัชนีที่สองจะต้องอยู่ใน: user_id เท่านั้นและจะใช้ในกรณีที่คุณกำลังค้นหาเฉพาะ user_id เท่านั้น (ซึ่งดัชนีแรกจะไม่ช่วยเนื่องจากลำดับของคีย์)
Yardboy

13
ในราง 4 การโยกย้ายจะต้องrails g migration create_restaurants_usersไม่มีโต๊ะที่ท้าย
Fa11enAngel

6
คุณยังสามารถใช้ราง g การโยกย้ายผู้ใช้ร้านอาหาร CreateJoinTableRestaurantUser อ่านguide.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

36

คำตอบที่นี่ค่อนข้างล้าสมัย ใน Rails 4.0.2 การย้ายข้อมูลของคุณใช้ประโยชน์จากcreate_join_tableไฟล์.

ในการสร้างการย้ายข้อมูลให้เรียกใช้:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

สิ่งนี้จะสร้างสิ่งต่อไปนี้:

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

หากคุณต้องการจัดทำดัชนีคอลัมน์เหล่านี้ให้ยกเลิกการใส่เครื่องหมายบรรทัดตามลำดับและคุณก็พร้อมที่จะไป!


2
โดยทั่วไปฉันจะยกเลิกการใส่ข้อคิดเห็นหนึ่งในบรรทัดดัชนีและเพิ่มunique: trueเข้าไป วิธีนี้จะป้องกันไม่ให้มีการสร้างความสัมพันธ์ที่ซ้ำกัน
Toby 1 Kenobi

26

เมื่อสร้างตารางการเข้าร่วมให้ใส่ใจกับข้อกำหนดที่ว่าตารางทั้งสองจะต้องแสดงตามลำดับตัวอักษรในชื่อ / คลาสการย้ายข้อมูล สิ่งนี้สามารถกัดคุณได้อย่างง่ายดายหากชื่อรุ่นของคุณคล้ายกันเช่น "abc" และ "abb" ถ้าคุณจะวิ่ง

rails g migration create_abc_abb_table

ความสัมพันธ์ของคุณจะไม่เป็นไปตามที่คาดหวัง คุณต้องใช้

rails g migration create_abb_abc_table

แทน.


1
อันไหนมาก่อน? foo หรือ foo_bar?
B Seven

1
ทำงานในคอนโซลราง: ["foo_bar", "foo", "foo bar"]. sort # => ["foo", "foo bar", "foo_bar"] Unix sort จะเหมือนกัน
Shadoath

6

สำหรับความสัมพันธ์ HABTM คุณต้องสร้างตารางการเข้าร่วม มีเพียงตารางเข้าร่วมและตารางนั้นไม่ควรมีคอลัมน์รหัส ลองย้ายข้อมูลนี้

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

คุณต้องตรวจสอบบทแนะนำเกี่ยวกับรางความสัมพันธ์นี้


ฉันไม่คิดว่าคุณต้องการแบบจำลองและฉันไม่เห็นอะไรในลิงค์เกี่ยวกับความต้องการแบบจำลองสำหรับความสัมพันธ์ HABTM
B Seven

1
เพื่อเพิ่มความเร็วในการสืบค้นข้อมูลให้เพิ่มดัชนีลงในช่อง id
Martin Röbert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.