两个相互排斥的多对多关系

时间:2017-12-28 16:16:02

标签: python sql django postgresql many-to-many

想象一下网上商店。你有货。有些商品有尺寸,有些没有。我有一张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_idsize_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?还有其他建议吗?

我已使用djangopythonpostgresql标记标记了该问题。那是因为那些是我现在正在使用的。但我并没有依赖任何特定语言,而是SQL。

UPD 我刚刚意识到我已经非规范化sizes表。那里大多有SML尺寸。

现在我看到四个选项:

  1. 我现在的方式。 Order.productsOrder.sizes似乎有效。他们得到非交叉产品组。但是数据库中存在不一致的可能性(orders_products.product_idorders_products.size_id都已设置或未设置。

  2. 建议使用maverick:通用外键。

  3. 规范化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被链接到产品没有的大小。

    在规范化表格的情况下,通用外键很可能不会这样做。

  4. 提取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
    

    关于通用外键的声明似乎也适用于此。

  5. 哪一个更好?

2 个答案:

答案 0 :(得分:1)

考虑在OrderProductProduct上使用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,
...

此外,对于没有大小的项目,我的名称为空名称。