Rails:多态关联,不同的选项取决于类型?

时间:2014-07-03 04:02:11

标签: ruby-on-rails activerecord database-design ruby-on-rails-4 model

我在Rails 4.1中构建了一个饮食分析应用程序。我有一个模型FoodEntry,它在一个简单的级别上具有quantity值,并引用FoodMeasure

class FoodEntry < ActiveRecord::Base
  belongs_to :food
  belongs_to :measure
end

然而,我实际上有两种不同类型的措施,标准通用措施(杯子,茶匙,克等)和特定于食物的措施(西兰花头,中型香蕉,大罐等) 。听起来像多态关联的情况吧? e.g。

class FoodEntry < ActiveRecord::Base
  belongs_to :food
  belongs_to :measure, polymorphic: true # Uses measure_id and measure_type columns
end

class StandardMeasure < ActiveRecord::Base
  has_many :food_entries, as: :measure
end

class FoodMeasure < ActiveRecord::Base
  has_many :food_entries, as: :measure
end

问题是,特定于食物的措施来自遗留数据库转储。这些记录由其food_iddescription的组合唯一标识 - 它们未提供单列主键(description本身并不唯一,因为那里是多种食物,具有相同的度量描述但不同的数字数据)。因为我导入了我的Rails Postgres数据库,所以我能够添加一个代理主键--Rails期望的自动递增整数id列。但是我不想在我的id模型中使用这个FoodEntry作为参考,因为当(外部提供的)数据更新时,保持参照完整性不变是一个相当大的挑战。我必须重新进口。基本上,这些ids完全可以更改,因此我更愿意直接引用food_iddescription

幸运的是,通过在协会上使用范围,在Rails中执行此操作并不困难:

class FoodEntry < ActiveRecord::Base
  belongs_to :food
  belongs_to :measure, ->(food_entry) { where(food_id: food_entry.food_id) }, primary_key: 'description', class_name: 'FoodMeasure'
  # Or even:           ->(food_entry) { food_entry.food.measures }, etc.
end

这产生了一个完全可以接受的查询:

> FoodEntry.first.measure
FoodMeasure Load (15.6ms)  SELECT  "food_measures".* FROM "food_measures"  WHERE "food_measures"."description" = $1 AND "food_measures"."food_id" = '123' LIMIT 1  [["description", "Broccoli head"]]

请注意,这假设measure_id在这种情况下是一个字符串列(因为description是一个字符串)。

相比之下StandardMeasure数据在我的控制之下并且没有引用Foods,所以简单地引用{{1}是完全合理的在这种情况下的列。

所以我的问题的症结在于:我需要一种方法让id仅引用一种类型的度量,就像我在上面所做的多态关联示例中那样。但是,我不知道我如何实现与FoodEntry模型相关的多态关联,因为它代表了:

  • 关联的measure需要通过范围引用,而FoodMeasure则不会。
  • 关联的StandardMeasure需要由字符串引用,而FoodMeasure由整数引用(并且引用的列具有不同的名称)。

如何协调这些问题?


修改:我想我应该解释为什么我不想在StandardMeasure上使用自动编号id作为FoodMeasures中的外键。更新数据集后,我的计划是:

  1. 将当前FoodEntries表重命名为food_measures(或其他)。
  2. 将新的数据集导入新的retired_food_measures表(带有一组新的自动编号ID)。
  3. 在这两个表之间运行连接,然后删除food_measures中的所有常用记录,因此它只有已退役的记录。
  4. 如果我通过retired_food_measuresfood_id引用这些指标,那么我就可以获得食品条目自动引用新记录的好处,因此给出了给定的更新数字数据测量。我可以指示我的应用程序在description表中搜索,如果在新的表中找不到引用的度量。

    这就是为什么我认为使用retired_food_measures列会使事情变得更复杂,为了获得相同的好处,我必须确保每个更新的记录都收到与id相同的id旧的,每个新记录都会收到一个新的未使用过的id,并且任何已退役的id都不会再次使用。

    还有另外一个我不想这样做的原因:订购。转储中的记录首先由food_id排序,但是任何给定food_id的度量都是非字母顺序但仍然是逻辑顺序我想保留。 id列可以优雅地实现此目的(因为ID在导入时按行顺序分配),但是当ids开始变得混乱时我就失去了这个好处。

    所以,是的,我确定我可以解决这些问题,但我不确定这样做有什么好处呢?

1 个答案:

答案 0 :(得分:0)

  

它对保持参照完整性构成了相当大的挑战   (外部提供的)数据更新时完好无损

这是一种幻觉。您可以完全控制代理人。您可以完全处理外部更新,无论它们是否存在。

这只是您想要自己的新名称的时候之一,在这种情况下,Measure,其中FoodMeasures和StandardMeasures是子类型。在所有三个模型/表中都有一个measure_id。您可以找到许多用于简化子类型约束的习语,例如使用类型标记。

如果你以这样的方式处理外部更新,以便这些对象也有这样的代理,那么你需要清楚地将这些PutativeFoodMeasures和FoodMeasures分开作为某些超类型PutativeOrProvenFoodMeasure和/或PutativeOrProvenMeasure的子类型。

编辑:

您的更新有帮助。你已经描述了我的所作所为。将旧图标映射到新图标并不困难;加入旧的&amp; new(food_id,description)并选择旧的id(不是food_id!)。你控制id;如果重复使用ID,那么它们如何才能解决问题呢?同样可以分类FoodMeasures;像你一样做。只有当您将它们与StandardMeasures混合使用时,您需要以不同的方式订购混合物;但无论如何,无论是否存在共享ID,您都会这样做。 (虽然&#34;多态:&#34;可能不是最佳的id共享设计。)

“措施”模型提供了措施;当你知道你有FoodMeasure或StandardMeasure时,你可以得到它的子类型特定部分。