Rails has_and_belongs_to_many di chuyển


119

Tôi có hai mô hình restaurantusertôi muốn thực hiện mối quan hệ has_and_belongs_to_many.

Tôi đã đi vào các tệp mô hình và thêm has_and_belongs_to_many :restaurantshas_and_belongs_to_many :users

Tôi cho rằng tại thời điểm này, tôi có thể làm điều gì đó giống như với Rails 3:

rails generate migration ....

nhưng mọi thứ tôi đã thử dường như đều thất bại. Tôi chắc rằng đây là một cái gì đó thực sự đơn giản, tôi mới làm quen với rails nên tôi vẫn đang học.

Câu trả lời:


260

Bạn cần thêm một bảng nối riêng chỉ có a restaurant_iduser_id(không có khóa chính), theo thứ tự bảng chữ cái .

Trước tiên, hãy chạy quá trình di chuyển của bạn, sau đó chỉnh sửa tệp di chuyển đã tạo.

Đường ray 3

rails g migration create_restaurants_users_table

Đường ray 4 :

rails g migration create_restaurants_users

Đường ray 5

rails g migration CreateJoinTableRestaurantUser restaurants users

Từ các tài liệu :

Ngoài ra còn có một trình tạo sẽ tạo ra các bảng tham gia nếu JoinTable là một phần của tên:


Tệp di chuyển của bạn (lưu ý :id => false; đó là thứ ngăn cản việc tạo khóa chính):

Đường ray 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

Đường ray 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_tosẽ tự động tạo các chỉ số cần thiết. def changesẽ tự động phát hiện chuyển tiếp hoặc di chuyển ngược, không cần lên / xuống.

Đường ray 5

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

Lưu ý: Ngoài ra còn có một tùy chọn cho tên bảng tùy chỉnh có thể được truyền dưới dạng tham số để create_join_table được gọi table_name. Từ các tài liệu

Theo mặc định, tên của bảng nối xuất phát từ sự kết hợp của hai đối số đầu tiên được cung cấp cho create_join_table, theo thứ tự bảng chữ cái. Để tùy chỉnh tên của bảng, hãy cung cấp tùy chọn: table_name:


8
@Dex - Chỉ vì tò mò, bạn có thể giải thích lý do tại sao bạn đang sử dụng chỉ mục kết hợp thứ hai, được xác định với thứ tự cột đảo ngược không? Tôi có ấn tượng rằng thứ tự cột không quan trọng. Tôi không có DBA, chỉ muốn nâng cao hiểu biết của riêng tôi. Cảm ơn!
flatjimbo

11
@Jimbo Bạn không cần nó theo cách đó, nó thực sự phụ thuộc vào truy vấn của bạn. Các chỉ mục được đọc từ trái sang phải vì vậy chỉ mục đầu tiên sẽ nhanh nhất nếu bạn đang tìm kiếm trên restaurant_id. Điều thứ hai sẽ hữu ích nếu bạn đang tìm kiếm user_id. Nếu bạn đang tìm kiếm trên cả hai, tôi sẽ nghĩ rằng cơ sở dữ liệu sẽ đủ thông minh để chỉ cần một. Vì vậy, tôi đoán cái thứ hai không thực sự cần phải được ghép. Đây chỉ là một ví dụ. Tuy nhiên, đây là một câu hỏi Rails, vì vậy việc đăng trong phần DB sẽ mang lại câu trả lời đầy đủ hơn.
Dex

3
Chỉ mục thứ hai có một số dư thừa - miễn là bạn đang truy vấn trên cả restaurant_id và user_id, thứ tự của chúng trong SQL của bạn không quan trọng và chỉ mục đầu tiên sẽ được sử dụng. Nó cũng sẽ được sử dụng nếu bạn chỉ truy vấn trên restaurant_id. Chỉ mục thứ hai chỉ cần ở trên: user_id và sẽ được sử dụng trong trường hợp bạn chỉ truy vấn trên user_id (chỉ mục đầu tiên sẽ không giúp được gì do thứ tự các khóa của nó).
Yardboy

13
Trong rails 4, việc di chuyển phải rails g migration create_restaurants_userskhông có bảng ở cuối.
Fa11enAngel

6
Bạn cũng có thể sử dụng rails g di chuyển người dùng nhà hàng CreateJoinTableRestaurantUser. Đọc Guide.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

36

Các câu trả lời ở đây là khá ngày tháng. Kể từ Rails 4.0.2, việc di chuyển của bạn sẽ được tận dụng create_join_table.

Để tạo quá trình di chuyển, hãy chạy:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Điều này sẽ tạo ra những điều sau:

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

Nếu bạn muốn lập chỉ mục các cột này, hãy bỏ ghi chú các dòng tương ứng và bạn đã sẵn sàng!


2
Tôi thường bỏ ghi chú một trong các dòng chỉ mục và thêm unique: truevào đó. Điều này sẽ ngăn các mối quan hệ trùng lặp được tạo ra.
Toby 1 Kenobi,

26

Khi tạo bảng nối, hãy chú ý đến yêu cầu hai bảng cần được liệt kê theo thứ tự bảng chữ cái trong tên / lớp di chuyển. Điều này có thể dễ dàng cắn bạn nếu tên kiểu máy của bạn giống nhau, ví dụ: "abc" và "abb". Nếu bạn chạy

rails g migration create_abc_abb_table

Các mối quan hệ của bạn sẽ không được như mong đợi. Bạn phải dùng

rails g migration create_abb_abc_table

thay thế.


1
Cái nào có trước? foo hay foo_bar?
B Bảy

1
Ran trong bảng điều khiển rails: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] Unix sắp xếp giống nhau.
Shadoath

6

Đối với các mối quan hệ HABTM, bạn cần tạo một bảng tham gia. Chỉ có bảng tham gia và bảng đó không được có cột id. Hãy thử di chuyển này.

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

Bạn phải kiểm tra hướng dẫn hướng dẫn mối quan hệ này


Tôi không nghĩ rằng bạn cần một mô hình và tôi không thấy bất kỳ điều gì trong liên kết về việc cần một mô hình cho mối quan hệ HABTM.
B Bảy

1
Để tăng tốc các truy vấn đã tạo, hãy thêm chỉ số vào các trường id.
Martin Röbert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.