我如何过滤 parent 在 parent child 关系模型上至少有一个活跃的 child

How can i filter parents that has at least one active child on parent child relationship model

我有“类别”模型,它有多个 parent/child 关系。

class Category(models.Model):
    pass

class CategoryParent(models.Model):
    parent_category = models.ForeignKey("Category", related_name="children", on_delete=models.CASCADE)
    child_category = models.ForeignKey("Category", related_name="parents", on_delete=models.CASCADE)
    is_active = models.BooleanField(verbose_name=_("Is active?"), default=True)


class CategoryExlusion(models.Model):
    browser = models.ForeignKey("Browser", on_delete=models.CASCADE)
    country = models.ForeignKey("Country", on_delete=models.CASCADE)
    category = models.ForeignKey("Category", on_delete=models.CASCADE)

我想列出所有 top-level 类别,这些类别至少有一个活跃的 child 类别。如果 CategoryExlusion 列表中有某个类别以及请求的用户的浏览器和国家/地区的记录,则表示该类别对该用户无效。我可以通过 request.user.browserrequest.user.country.

访问用户的浏览器和国家/地区

我正在处理的最困难的问题是我的 child 有可能 children.

例如,如果 B、X、D 和 Y 对于用户的浏览器和国家不活跃,即使 A 对于用户的浏览器和国家是活跃的,QuerySet 也不会返回 A,因为没有任何活跃的 child 为 A.

但是,至少有一个活动类别(例如 x 可能对用户的浏览器和国家/地区是活动的),A 将由 QuerySet 返回。

如您所述,这可能很难用 django-orm 实现,因为 tree-like 结构。我认为您最好的选择是使用 Django 的 Raw SQL queries to perform a Recursive Query。使用递归查询,您可以 select 特定 top-level 类别的所有后代(children 和 grand-children,等等)。然后您可以过滤掉所有在排除列表中或不活动的 children。如果至少有一个剩余后代,则顶级类别保留在查询集中。

我还没有测试过,但我认为针对特定顶级类别的递归查询看起来像这样:


team = Category.objects.raw('''
    WITH RECURSIVE descendents(id, is_active, child_category, parent_category) AS (
          SELECT id, is_active, child_category, parent_category 
          FROM category
          JOIN category_parent as children on category_parent.parent_category = category.id
          WHERE id = <id of the top level category>
        UNION ALL
          SELECT id, is_active, child_category, parent_category
          FROM descendents 
          JOIN parent_category as children on children.parent_category = descendents.id
        )
    SELECT * FROM descendents
''')

查询分解


SELECT id, is_active, child_category, parent_category 
          FROM category
          JOIN category_parent as children on category_parent.parent_category = category.id
          WHERE id = <id of the top level category>

这是非递归项,select直接children顶级类别


UNION ALL
          SELECT id, is_active, child_category, parent_category
          FROM descendents 
          JOIN parent_category as children on children.parent_category = descendents.id

这是递归项,self-referencing是非递归项,将select下一代children。这将迭代直到树中没有 children。


您可以使用 Django's Subquery 对每个顶级类别使用此递归查询。但是此时您最好在 python 中执行此操作,对每个顶级类别执行一个递归查询。根据您的数据库和类别数量,您可能会遇到性能问题,因为 back-and-forths 与数据库