Django 树表的复杂分区和排序

问题描述 投票:0回答:2

我使用 Django 和 PostgreSQL 作为我的应用程序的后端堆栈,该应用程序的主要功能是一个复杂的多级表,显示不同产品的带注释的时间序列数据。

所以基本上我定义了 2 个模型,一个是 Product,另一个是Timestamp,而产品模型是分层的,并使用 MPTT 库来实现树结构。 每个产品可以有多个子项,最大深度为 4 级,并且具有多个时间戳,描述它们在特定日期的表现数据。因此,我基本上是在查询集中注释每个产品的所有时间序列数据,并且工作完美。 我面临的问题是我需要通过注释值动态地订购产品,而不破坏它们在层次结构和/或父子关系中的位置。当我使用一些基本的 order_by("tree_id", "level" ...) 之类的方法时,父子关系被“覆盖”。另外重要是每个产品根都有它自己的tree_id并且是一个单独的树

模型.py

class Product(MPTTModel):
    owner = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
    budget = models.FloatField(null=True)
    creation_date = models.CharField(max_length=35, null=True)
    product_type = models.CharField(max_length=35, null=True)
    last_updated = models.DateTimeField(null=True)
    name = models.CharField(max_length=35, null=True)
    parent = parent = TreeForeignKey('self', on_delete=models.CASCADE,
                            null=True, blank=True)

class Timestamp(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
    viewed = models.IntegerField(default=0)
    bought = models.IntegerField(default=0)
    shown = models.IntegerField(default=0)
    date = models.DateField(null=True)

下面可以看到表格外观的一个非常基本的模型。enter image description here

我当前的查询集排序功能无法正常工作,可以在此处看到

  filters = self.request.GET
  d_start, d_end = filters["date_start"], filters["date_end"]
  

  qs=Product._tree_manager.get_queryset_descendants(Product.objects.filter(
     Exists(Timestamp.objects.filter(
         date__gte=d_start,
         date__lte=d_end,
         product=OuterRef("pk"))
         ), level=0), include_self=True).annotate(
            viewed=(Sum("timestamp__viewed")),
            bought=(Sum("timestamp__bought")),
            shown=(Sum("timestamp__shown"))).order_by("tree_id", "level", "bought")

结果如下所示: enter image description here

我怀疑该解决方案需要一些原始 SQL,也许需要分区与排序相结合。

我期待您的答复并提前致谢。

python sql django database postgresql
2个回答
3
投票

postgreSQL 的递归 CTE 正是我所需要的。

Django 的 ORM 非常可靠和健壮,但它也有其局限性。 复杂的、依赖于体系结构的功能(例如递归查询)无法使用 ORM 来实现 - 至少在我写这个答案时不能实现。

因此,这是一个需要我编写原始 SQL 的场景。 我无法在此处包含 SQL 代码,因为它包含合理的后端信息和逻辑。

但是如果您必须在 Django 中处理分层模型/查询,我可以告诉您一些事情。

  • 如果性能是首要任务,请勿使用MpttTreeBeard等库。它们都非常慢,并且在我的场景中不适合生产使用,因为加载时间较长,用户体验会受到极大影响。

  • 不要犹豫使用原始 SQL 查询,因为 Django 提供了 ORM。 迟早你会遇到 ORM 无法解决的问题,因为它具有高抽象级别

  • 不要浪费宝贵的时间尝试重新发明轮子

  • 如果您需要使用关系数据库来解决此问题(无论出于何种原因),请使用PostgreSQL,因为它提供了解决此问题的最佳性能、功能和扩展(我个人认为 PostgreSQL 是迄今为止最好的数据库)最好的 SQL-DB atm。)

  • 使用 ltree 扩展进行查询。它非常直观且易于理解,您可以在完整的postgres-docs中阅读它。

  • 如果您必须在不破坏层次结构的情况下对兄弟姐妹进行排序,请使用ArrayROW OVER结合使用。

当我从

Mptt 迁移到 CTEs 时,在我的场景中,整体性能提高了约 10 倍。

有用的链接:


0
投票

Django 不支持分区,如果你使用 django-postgres-extra 这样的模块,需要注意的是你不要在表上使用任何外键约束,因为 postgres 本身不提供对分区和外键的支持key ,您始终可以在代码级别进行额外检查以确保引用完整性。 现在取决于您是否要删除外键或分区。

© www.soinside.com 2019 - 2024. All rights reserved.