想象一下网上商店。你有货。有些商品有尺寸,有些没有。我有一张orders
表:
id int not null,
...
orders_products
表:
order_id int not null,
product_id int null,
size_id int null,
...
products
表:
id int not null,
...
sizes
表:
id int not null,
product_id int not null,
...
现在,product_id
或size_id
不为空。换句话说,主键是order_id
+(product_id
xor size_id
)。不是两个。
在Django的术语中将是:
class OrderProduct(models.Model):
product = models.ForeignKey(Product, null=True, on_delete=models.CASCADE)
size = models.ForeignKey(Size, null=True, on_delete=models.CASCADE)
order = models.ForeignKey('Order', on_delete=models.CASCADE)
amount = models.PositiveSmallIntegerField()
class Order(models.Model):
products = models.ManyToManyField(Product, through=OrderProduct, related_name='orders')
sizes = models.ManyToManyField(Size, through=OrderProduct, related_name='orders')
...
至少那就是我现在所拥有的。但我不喜欢在orders_products
表中有两个互斥的外键。或Order
模型中的两个属性。其中一个(sizes
)可能是多余的。
因此,我可能必须从sizes
模型中删除Order
属性。是吗?或者我应该在product_id
表中使orders_products
不为空?只有当产品有不同尺寸时才size_id
?还有其他建议吗?
我已使用django
,python
,postgresql
标记标记了该问题。那是因为那些是我现在正在使用的。但我并没有依赖任何特定语言,而是SQL。
UPD 我刚刚意识到我已经非规范化sizes
表。那里大多有S
,M
,L
尺寸。
现在我看到四个选项:
我现在的方式。 Order.products
和Order.sizes
似乎有效。他们得到非交叉产品组。但是数据库中存在不一致的可能性(orders_products.product_id
和orders_products.size_id
都已设置或未设置。
建议使用maverick:通用外键。
规范化sizes
表(多对多关系):
products
表:
id int not null,
...
products_sizes
表:
product_id int not null,
size_id int not null,
...
sizes
表:
id int not null,
...
然后,用orders_products
这样表:
order_id int not null,
product_id int null not null,
size_id int null,
...
更有意义。好吧,对于有大小的产品,orders_products.size_id
仍有可能为空。并且orders_products.size_id
被链接到产品没有的大小。
在规范化表格的情况下,通用外键很可能不会这样做。
提取product_variants
表(消费者基本上购买的):
products
:
id int not null,
...
sizes
:
id int not null,
...
product_variants
:
id int not null,
product id int not null,
size_id int null
orders_products
:
order_id int not null,
productvariant_id int not null,
amount int not null
关于通用外键的声明似乎也适用于此。
哪一个更好?
答案 0 :(得分:1)
考虑在OrderProduct
和Product
上使用Generic Foreign Key ProductSize
。它存储对象类型和对象id,它在两个外键之间提供互斥。
class OrderProduct(models.Model):
...
limit = models.Q(app_label = 'app', model = 'Product') | models.Q(app_label = 'app', model = 'ProductSize')
content_type = models.ForeignKey(ContentType, limit_choices_to = limit)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
答案 1 :(得分:0)
我决定解决我的最后一个想法。通过创建中间sizes
表来规范化productvariants
表,该表包含代表客户购买的实体。
orders
表:
id int not null,
...
orders_productvariants
表:
order_id int not null,
productvariant_id int not null,
...
productvariants
表:
id int not null,
product_id int not null,
size_id int not null,
...
products
表:
id int not null,
...
sizes
表:
id int not null,
...
此外,对于没有大小的项目,我的名称为空名称。