【问题标题】:Rails: polymorphic association, different options depending on type?Rails:多态关联,不同的选项取决于类型?
【发布时间】:2014-08-24 01:06:54
【问题描述】:

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

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

但我实际上有两种不同类型的量度,标准的通用量度(杯子、茶匙、克等)和特定于食物的量度(西兰花头、中型香蕉、大罐头等) .听起来像是多态关联的案例,对吧?例如

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 列。但是我不想在我的FoodEntry 模型中使用这个id 作为参考,因为当更新(外部提供的)数据并且我必须重新导入时,它对保持参照完整性构成了相当大的挑战。基本上,那些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,因此在这种情况下简单地引用 id 列是非常有意义的。 p>

所以我的问题的症结在于:我需要一种方法让FoodEntry 仅引用一种类型的度量,就像我在上面制作的多态关联示例中那样。但是我不知道如何针对我的measure 模型实现多态关联,因为就目前而言:

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

如何协调这些问题?


编辑:我想我应该解释一下为什么我不想在FoodMeasures 上使用自动编号id 作为我在FoodEntries 中的外键。当数据集更新时,我的计划是:

  1. 将当前的food_measures 表重命名为retired_food_measures(或其他)。
  2. 将新数据集导入到新的 food_measures 表中(使用新的自动编号 ID 集)。
  3. 在这两个表之间运行一个连接,然后删除retired_food_measures 中的所有公共记录,这样它就只有退休的记录了。

如果我通过food_iddescription 引用这些度量,那么我将获得食物条目自动引用新记录以及因此给定度量的任何更新数字数据的好处。如果在新表中找不到引用的度量,我可以指示我的应用程序在 retired_food_measures 表中进行搜索。

这就是为什么我认为使用id 列会使事情变得更复杂,为了获得相同的好处,我必须确保每条更新的记录都收到与旧记录相同的id,每条新记录记录收到了一个新的 not-used-before id,并且任何已停用的 id 将不再使用。

我不想这样做还有另一个原因:订购。转储中的记录首先按food_id 排序,但是任何给定food_id 的度量都是非字母顺序但我想保留的逻辑顺序。 id 列可以很好地达到这个目的(因为 id 在导入时按行顺序分配),但是当 id 开始被弄乱时,我就失去了这个好处。

所以是的,我确信我可以针对这些问题实施解决方案,但我不确定这样做是否值得?

【问题讨论】:

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


【解决方案1】:

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

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

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

如果您以这样一种方式处理外部更新,以便此类对象也具有此类代理项,那么您需要将此类 PutativeFoodMeasures 和 FoodMeasures 明确区分为某些超类型 PutativeOrProvenFoodMeasure 和/或 PutativeOrProvenMeasure 的子类型。

编辑:

您的更新有帮助。你已经描述了我的所作所为。将旧的 id 映射到新的 id 并不难;加入新旧(food_id,description)并选择旧id(不是food_id!)。您控制 ID;与不存在的 id 相比,重用 id 又有什么关系呢?同样用于对 FoodMeasures 进行分类;做你想做的事。只有当您将它们与 StandardMeasures 混合时,您才需要以不同的方式订购混合物;但是无论是否存在共享 ID,您都会这样做。 (虽然“多态:”可能不是最好的 id 共享设计。)

度量模型提供度量;当你知道你有一个 FoodMeasure 或 StandardMeasure 时,你可以得到它的子类型特定部分。

【讨论】:

  • 我很难解释你的回答。你能举个例子说明你会怎么做吗?
  • 你读过布兰科的评论链接吗?重新措施:您的措施-FoodMeasures-StandardMeasures 是他的通知用户组。
猜你喜欢
  • 2021-10-11
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
相关资源
最近更新 更多