Railsy mają_and_belongs_to_many migrację


119

Mam dwa modele restauranti userchcę wykonać relację has_and_belongs_to_many.

Przejrzałem już pliki modeli i dodałem has_and_belongs_to_many :restaurantsihas_and_belongs_to_many :users

Zakładam, że w tym momencie powinienem być w stanie zrobić coś podobnego do Rails 3:

rails generate migration ....

ale wszystko, czego próbowałem, zawodziło. Jestem pewien, że jest to coś naprawdę prostego. Jestem nowy w railach, więc wciąż się uczę.

Odpowiedzi:


260

Musisz dodać oddzielną tabelę łączenia zawierającą tylko restaurant_idi user_id(bez klucza podstawowego), w kolejności alfabetycznej .

Najpierw uruchom migracje, a następnie edytuj wygenerowany plik migracji.

Szyny 3

rails g migration create_restaurants_users_table

Szyny 4 :

rails g migration create_restaurants_users

Szyny 5

rails g migration CreateJoinTableRestaurantUser restaurants users

Z dokumentów :

Istnieje również generator, który utworzy tabele złączeń, jeśli JoinTable jest częścią nazwy:


Twój plik migracji (zwróć uwagę na :id => false; to jest to, co zapobiega utworzeniu klucza podstawowego):

Szyny 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

Szyny 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_toautomatycznie utworzy niezbędne indeksy. def changeautomatycznie wykryje migrację do przodu lub do tyłu, bez potrzeby wykonywania operacji w górę / w dół.

Szyny 5

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

Uwaga: istnieje również opcja niestandardowej nazwy tabeli, którą można przekazać jako parametr do create_join_table o nazwie table_name. Z dokumentów

Domyślnie nazwa tabeli łączenia pochodzi z sumy pierwszych dwóch argumentów podanych do create_join_table, w porządku alfabetycznym. Aby dostosować nazwę tabeli, podaj opcję: nazwa_tabeli:


8
@Dex - Tak z ciekawości, czy możesz wyjaśnić, dlaczego używasz drugiego indeksu złożonego, zdefiniowanego z odwróconą kolejnością kolumn? Miałem wrażenie, że kolejność kolumn nie ma znaczenia. Nie jestem DBA, chcę tylko pogłębić własne zrozumienie. Dzięki!
plainjimbo

11
@Jimbo Nie potrzebujesz tego w ten sposób, to naprawdę zależy od twoich zapytań. Indeksy czytane są od lewej do prawej, więc pierwszy będzie najszybszy, jeśli szukasz restaurant_id. Drugi pomoże, jeśli szukasz dalej user_id. Jeśli szukasz w obu, myślę, że baza danych byłaby wystarczająco inteligentna, aby potrzebować tylko jednej. Więc myślę, że drugi nie musi być tak naprawdę złożony. To był raczej przykład. Było to jednak pytanie Railsów, więc publikowanie w sekcji DB dałoby pełniejszą odpowiedź.
Dex

3
Drugi indeks ma pewną redundancję - o ile odpytujesz zarówno o identyfikator_restauracji, jak i identyfikator_użytkownika, ich kolejność w kodzie SQL nie ma znaczenia i zostanie użyty pierwszy indeks. Będzie również używany, jeśli odpytujesz tylko na restaurant_id. Drugi indeks musi znajdować się tylko na: user_id i będzie używany w przypadkach, gdy odpytujesz tylko o user_id (w czym pierwszy indeks nie pomoże ze względu na kolejność jego kluczy).
Yardboy,

13
W szynach 4 migracja musi na końcu odbywać się rails g migration create_restaurants_usersbez stołu .
Fa11enAngel

6
Możesz również użyć rails g migracja CreateJoinTableRestaurantUser użytkownika restauracji. Przeczytaj guide.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

36

Odpowiedzi tutaj są dość przestarzałe. Począwszy od Rails 4.0.2, Twoje migracje wykorzystują create_join_table.

Aby utworzyć migrację, uruchom:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Spowoduje to wygenerowanie:

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

Jeśli chcesz zindeksować te kolumny, odkomentuj odpowiednie wiersze i gotowe!


2
Generalnie odkomentowałbym jedną z linii indeksu i dodałbym unique: truedo niej. Zapobiegnie to tworzeniu zduplikowanych relacji.
Toby 1 Kenobi

26

Podczas tworzenia tabeli łączenia należy zwrócić szczególną uwagę na wymaganie, aby dwie tabele były wymienione w kolejności alfabetycznej w nazwie / klasie migracji. Może to łatwo Cię ugryźć, jeśli nazwy modeli są podobne, np. „Abc” i „abb”. Gdybyś miał uciekać

rails g migration create_abc_abb_table

Twoje relacje nie będą działać zgodnie z oczekiwaniami. Musisz użyć

rails g migration create_abb_abc_table

zamiast.


1
Co jest pierwsze? foo czy foo_bar?
B Seven

1
Uruchomiono w konsoli railsów: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] Sortowanie w systemie Unix wygląda tak samo.
Shadoath,

6

W przypadku relacji HABTM musisz utworzyć tabelę łączenia. Jest tylko tabela złączeń i ta tabela nie powinna mieć kolumny id. Spróbuj tej migracji.

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

Musisz sprawdzić samouczki przewodnika po szynach relacji


Myślę, że nie potrzebujesz modelki i nie widzę nic w linku o potrzebie modelu do związku HABTM.
B Seven

1
Aby przyspieszyć generowane zapytania, dodaj indeksy do pól id.
Martin Röbert
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.