Rails has_and_belongs_to_many миграция

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 ....

но все, что я пробовал, похоже, терпит неудачу. Я уверен, что это что-то действительно простое. Я новичок в рельсах, поэтому я все еще учусь.

dbslone
источник

Ответы:

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:

Dex
источник
8
@Dex - Просто из любопытства, не могли бы вы объяснить, почему вы используете второй составной индекс, определенный с обратным порядком столбцов? У меня создалось впечатление, что порядок столбцов не имеет значения. Я не администратор баз данных, просто хочу углубить свое понимание. Спасибо!
plainjimbo
11
@Jimbo Тебе это не нужно, это действительно зависит от твоих запросов. Индексы читаются слева направо, поэтому первый из них будет самым быстрым при поиске restaurant_id. Второй поможет, если вы ищете user_id. Если вы ищете на обоих, я думаю, что база данных будет достаточно умной, чтобы нуждаться только в одном. Так что я думаю, что второй вариант действительно не нужно усложнять. Это был скорее просто пример. Однако это был вопрос Rails, поэтому публикация в разделе БД даст более полный ответ.
Dex
3
Второй индекс имеет некоторую избыточность - пока вы запрашиваете и restaurant_id, и user_id, их порядок в вашем SQL не имеет значения, и будет использоваться первый индекс. Он также будет использоваться, если вы запрашиваете только restaurant_id. Второй индекс должен быть включен только: user_id, и он будет использоваться в тех случаях, когда вы запрашиваете только user_id (с чем первый индекс не поможет из-за порядка его ключей).
Yardboy
13
В направляющих 4 миграция должна быть rails g migration create_restaurants_usersбез таблицы в конце.
Fa11enAngel
6
Вы также можете использовать rails g migration CreateJoinTableRestaurantUser restaurant user. Прочтите guides.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

вместо.

shacker
источник
1
Что на первом месте? foo или foo_bar?
B Seven
1
Запускаем консоль rails: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] Сортировка Unix работает точно так же.
Шадоат
6

Для отношений HABTM вам необходимо создать таблицу соединений. Есть только таблица соединений, и в этой таблице не должно быть столбца id. Попробуйте эту миграцию.

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.
Мартин Рёберт