【问题标题】:Django model relationship best solutionDjango模型关系最佳解决方案
【发布时间】:2017-05-25 12:13:12
【问题描述】:

我有以下型号:

BasketFruit

一个Basket 可以有多个Fruit 对象,一个Fruit 属于一个Basket。每个Fruit 都可以手动排序,以便在列出它们时显示它们的保存顺序。到目前为止,一切都很好。

class Basket (models.Model):
    # A fruit basket
    name = models.CharField(max_length=100)
    is_virtual = models.BooleanField(default=False)
    # This is just a badly constructed example of how a list
    # of basket primary keys are saved when creating the virtual basket
    # This is only for demonstration purposes to solve the overall modelling issue
    virtual_baskets = models.TextField(null=True)

class Fruit (models.Model):
    # The basket this fruit belongs to
    basket = models.ForeignKey("Basket", related_name="fruits", on_delete=models.CASCADE)

    # Manually sorted order
    order = models.PositiveIntegerField(default=0)

但是,Basket 上有一个名为 is_virtual 的标志,它允许用户从现有的真实篮子中创建虚拟组合篮子,您最终会稍微违反规则:虚拟篮子实际上可以组合两个现有的Baskets,因此来自两个篮子的Fruits。我需要能够允许用户手动对这些篮子中的Fruits 进行排序和重新排序,而不会覆盖其原始篮子中的排序。

举个例子:

Basket A 依次包含:苹果、香蕉

Basket B 依次包含:橙子、葡萄

我现在希望创建一个虚拟的Basket C 来显示 A 和 B 的果实,如下所示:

苹果、葡萄、香蕉、橙子

需要注意的是:虚拟篮子只是简单地组合了来自其他各种篮子的结果。对于用户来说,它仍然是一个有效的篮子,他们应该能够重新订购水果。

如何建模这种排序关系?我正在考虑添加一个新模型:

class FruitAppearance (models.Model):
    # The basket this appearance is tied to
    basket = models.ForeignKey("Basket", related_name="manually_sorted_fruits", on_delete=models.CASCADE)

    # The fruit it represents
    fruit = models.ForeignKey("Fruit", on_delete=models.CASCADE)

    # Order
    order = models.PositiveIntegerField(default=0)

我想对了吗?有没有更好的办法?我应该在现有的 FruitBasket 模型之一中使用 ManyToManyField 吗?

【问题讨论】:

  • 水果属于一个篮子,不管它是不是虚拟的?
  • 正确,它只属于一个真实的篮子,但可以在虚拟篮子中以不同的方式呈现
  • 当然也可以保存虚拟篮子,手动点水果应该坚持下去
  • 是否可以有 virtual_basket_a -> apple , virtual_basket_b -> apple, my_basket -> apple ?
  • 基本上,如果您创建一个虚拟篮子,它将始终放在已经属于真实篮子的水果上。所以真正篮子里的所有水果都会显示出来。这里唯一增加的功能是用户可以对他们创建的虚拟篮子中每个水果的显示顺序进行排序。

标签: python django django-models django-rest-framework


【解决方案1】:
class Basket(models.Model):
    name = models.CharField(max_length=100)
    fruits = models.ManyToMany("Fruit", through="FruitBasket")
    ...

class Fruit(models.Model):
    ...

class FruitBasket(models.Model:
    basket = models.ForeignKey("Basket", on_delete=models.CASCADE)
    fruit = models.ForeignKey("Fruit", on_delete=models.CASCADE)
    order = models.PositiveIntegerField(default=0)
    ...

这样您将有一些数据冗余,但可以为每个篮子中的水果设置不同的订单号。

【讨论】:

  • 谢谢,虽然我很疑惑,这里的 FruitBasket 和我创建的 FruitAppearance 模型类完全一样。我看到的唯一区别是篮子中增加的冗余(如你所说)......不会有什么不同吗?
  • 是的,水果冗余正是允许您为不同篮子中的单个水果设置不同订单值的原因(例如,柠檬在篮子 A 中的订单级别为 1,但在篮子 B 中的订单级别为 4)。如果没有这种冗余,您将只能为水果设置固定的订单级别。
  • 我明白了.. 我想在 db 模型级别,该类单独创建所需的中间表,但是从 Bucket 访问的“fruits”属性将以编程方式允许我们访问/设置那些特性。当然。谢谢,我认为这很不幸,但这是模型中唯一合理的补充,可以让它工作!
  • 虽然想想,这可能行不通。 VirtualBucket.fruits 将返回 None 因为它实际上不拥有任何任务,在运行时我们使用一组复杂的过滤器来查找和组合来自各种存储桶的结果。因此,虚拟存储桶除了用作包含元数据的占位符之外没有其他用途,而是被视为普通存储桶,因此它可以正常显示在可用存储桶列表中。
【解决方案2】:

我建议如下。 将模型 Basket 和 Virtual Basket 分开,因此删除字段 virtual。

所以每个水果可以属于一个篮子(ForeignKey),但同时可以属于多个虚拟篮子(ManyToManyField)。在 manytomany 字段中使用参数 throught 并包含另一个模型,该模型将只存储特定虚拟篮子的水果顺序。

class Fruit (models.Model):
    # The basket this fruit belongs to
    basket = models.ForeignKey("Basket", related_name="fruits", on_delete=models.CASCADE)
    virtual_basket = models.ManyToManyField(Fruit, through='FruitOrdering')

    # Manually sorted order
    order = models.PositiveIntegerField(default=0)

【讨论】:

  • 遗憾的是,这不是一个可行的解决方案。水果将永远只存在并由实际创建的真实篮子拥有。由于上面是真实应用程序尝试执行的简单示例,因此真实应用程序会创建复杂的搜索过滤器并从现有的内容列表中创建智能列表。想想一个联系人应用程序,其中联系人来自不同的帐户/提供商,我们从中创建虚拟列表。每个提供商都对联系人进行排序,然后我们需要允许在我们的智能列表表示中重新排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-21
  • 2020-04-12
  • 1970-01-01
  • 2014-07-05
  • 2013-06-11
  • 1970-01-01
相关资源
最近更新 更多