我如何过滤 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.browser
和 request.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 与数据库
我有“类别”模型,它有多个 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.browser
和 request.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 与数据库