我使用 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)
我当前的查询集排序功能无法正常工作,可以在此处看到
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")
我怀疑该解决方案需要一些原始 SQL,也许需要分区与排序相结合。
我期待您的答复并提前致谢。
postgreSQL 的递归 CTE 正是我所需要的。
Django 的 ORM 非常可靠和健壮,但它也有其局限性。 复杂的、依赖于体系结构的功能(例如递归查询)无法使用 ORM 来实现 - 至少在我写这个答案时不能实现。
因此,这是一个需要我编写原始 SQL 的场景。 我无法在此处包含 SQL 代码,因为它包含合理的后端信息和逻辑。
但是如果您必须在 Django 中处理分层模型/查询,我可以告诉您一些事情。
如果性能是首要任务,请勿使用Mptt或TreeBeard等库。它们都非常慢,并且在我的场景中不适合生产使用,因为加载时间较长,用户体验会受到极大影响。
不要犹豫使用原始 SQL 查询,因为 Django 提供了 ORM。 迟早你会遇到 ORM 无法解决的问题,因为它具有高抽象级别。
不要浪费宝贵的时间尝试重新发明轮子。
如果您需要使用关系数据库来解决此问题(无论出于何种原因),请使用PostgreSQL,因为它提供了解决此问题的最佳性能、功能和扩展(我个人认为 PostgreSQL 是迄今为止最好的数据库)最好的 SQL-DB atm。)
使用 ltree 扩展进行查询。它非常直观且易于理解,您可以在完整的postgres-docs中阅读它。
如果您必须在不破坏层次结构的情况下对兄弟姐妹进行排序,请使用Array与ROW OVER结合使用。
Mptt 迁移到 CTEs 时,在我的场景中,整体性能提高了约 10 倍。
有用的链接:
Django 不支持分区,如果你使用 django-postgres-extra 这样的模块,需要注意的是你不要在表上使用任何外键约束,因为 postgres 本身不提供对分区和外键的支持key ,您始终可以在代码级别进行额外检查以确保引用完整性。 现在取决于您是否要删除外键或分区。