我在Rails 4.1中构建了一个饮食分析应用程序。我有一个模型FoodEntry
,它在一个简单的级别上具有quantity
值,并引用Food
和Measure
:
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_id
和description
的组合唯一标识 - 它们未提供单列主键(description
本身并不唯一,因为那里是多种食物,具有相同的度量描述但不同的数字数据)。因为我导入了我的Rails Postgres数据库,所以我能够添加一个代理主键--Rails期望的自动递增整数id
列。但是我不想在我的id
模型中使用这个FoodEntry
作为参考,因为当(外部提供的)数据更新时,保持参照完整性不变是一个相当大的挑战。我必须重新进口。基本上,这些ids
完全可以更改,因此我更愿意直接引用food_id
和description
。
幸运的是,通过在协会上使用范围,在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
中的外键。更新数据集后,我的计划是:
FoodEntries
表重命名为food_measures
(或其他)。retired_food_measures
表(带有一组新的自动编号ID)。food_measures
中的所有常用记录,因此它只有已退役的记录。如果我通过retired_food_measures
和food_id
引用这些指标,那么我就可以获得食品条目自动引用新记录的好处,因此给出了给定的更新数字数据测量。我可以指示我的应用程序在description
表中搜索,如果在新的表中找不到引用的度量。
这就是为什么我认为使用retired_food_measures
列会使事情变得更复杂,为了获得相同的好处,我必须确保每个更新的记录都收到与id
相同的id
旧的,每个新记录都会收到一个新的未使用过的id
,并且任何已退役的id
都不会再次使用。
还有另外一个我不想这样做的原因:订购。转储中的记录首先由food_id
排序,但是任何给定food_id
的度量都是非字母顺序但仍然是逻辑顺序我想保留。 id
列可以优雅地实现此目的(因为ID在导入时按行顺序分配),但是当ids开始变得混乱时我就失去了这个好处。
所以,是的,我确定我可以解决这些问题,但我不确定这样做有什么好处呢?
答案 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时,你可以得到它的子类型特定部分。