Создание таблицы соединений в Rails с одной моделью и двумя атрибутами в другой модели

#ruby-on-rails #model #associations #has-and-belongs-to-many

#ruby-on-rails #Модель #ассоциации #имеет-и-принадлежит-многим

Вопрос:

Попытка создать таблицу соединений, которая принимает два атрибута в одной модели и связывает их с идентификатором второй модели. Невозможно использовать идентификатор первой модели из-за постоянного процесса обновления, который переупорядочивает строки в таблице этой модели (ConstantlyRefreshingModels).

 class ConstantlyRefreshingModels < ApplicationRecord
  attr_accessor :home_id, :chair, :floor, :number 
  has_and_belongs_to_many :family
end 

class Family < ApplicationRecord
  attr_accessor :bread_winner
  has_and_belongs_to_many :constantlyrefreshingmodel
end

class CreateFamilyModelThatConstantlyRefreshesJoinTable < ActiveRecord::Migration[5.0]
  def change
    create_join_table :families, :constantlyrefreshingmodel
  end
end
  

Ищем таблицу соединений, которая выглядит следующим образом:

 home_id | number | family_id
-----------------------------
    2   | '3003' |    4
    1   | '2100' |    1
  

И затем это работает для предоставления данных:

 new_home = constantlyrefreshingmodel.where(home_id: 2)
new_home.families == 4 == true
  

Есть предложения по созданию таблицы соединений и объявлению ассоциаций?

Комментарии:

1. Какую версию Rails вы используете?

2. «Невозможно использовать идентификатор первой модели из-за постоянного процесса обновления, который переупорядочивает строки в таблице этой модели» — ну и как вы должны связать две таблицы вместе, если нет какого-либо стабильного идентификатора?

3. Это даже не проблема Rails. Это проблема проектирования базы данных. Чтобы связать таблицу B с таблицей A — B должен иметь возможность ссылаться на A. Если по какой-либо причине вы не можете использовать автоматически увеличивающиеся первичные ключи по умолчанию, тогда вам нужно использовать UUID или какой-либо другой тип столбца, который можно использовать для внешних ключей вместо этого.

4. @s3tjan Rails 5.1

5. @max Я согласен, было бы идеально, если бы основные идентификаторы ConstantlyRefreshingModels были надежными, но с технологией, которую это представляет в реальном мире, мы используем комбинацию :home_id и :number , чтобы знать, как обновлять наши записи, поскольку они имеют большее значение для наших клиентов и разработчиков.

Ответ №1:

Вам нужен has_many :through Association не a has_and_belongs_to_many , поскольку у вас есть дополнительные атрибуты ( :number ) в модели соединения

Вы должны use has_many :through , если вам нужны проверки, обратные вызовы или дополнительные атрибуты в модели соединения. Раздел 2.8

Попробуйте этот подход:

 class Home < ApplicationRecord
  has_many :points
  has_many :families, through: :points
end

# Point is your join model, AKA ConstantlyRefreshingModel
class Point < ApplicationRecord
  belongs_to :family
  belongs_to :home

  validates :points, presence: true # Or some other validation, :number in your question.
end

class Family < ApplicationRecord
  has_many :points
  has_many :houses, through: :points
end
  

Миграция таблицы соединений:

 class CreatePoints < ActiveRecord::Migration[5.2]
  def change
    create_table :points do |t|
      t.references :home,   foreign_key: true, null: false
      t.references :family, foreign_key: true, null: false
      t.string     :points, null: false

      t.timestamps
    end
  end
end
  

Например, имея следующие данные:

 home_1 = Home.create(id: 1)  # home with an id of 1
home_2 = Home.create(id: 2)  # home with an id of 2

family_4 = Family.create(id: 4) # family with an id of 4
family_1 = Family.create(id: 1) # family with an id of 1

Point.create(points: "3003", home: home_2, family: family_4)
Point.create(points: "2100", home: home_1, family: family_1)
  

Затем:

 points = Point.where(home: home_2)  # you get a collection of all points assigned to house with ID: 2
points.first.family                 # family with an id of 4
  

Чтобы показать, что :home с id: 2 и числом 3003 указывает на семейство, в котором id: 4

 home = Home.find(2)
home.families.ids # 4
home.points.first # 3003
  

Чтобы предоставить семейство с id: 4 , оно связано с домом с идентификатором 2 и номером 3003.

 family = Family.find(4)
family.homes.ids     # 2
family.points.first  # 3003
  

Кроме того, attr_accessor , attr_reader и attr_writer в Rails 5.1 не рекомендуются. Смотрите этот пост

Комментарии:

1. Спасибо! В итоге я создал модель соединения и использовал has many: through, как вы рекомендовали. Хороший момент в конфигурациях attr.