mysql - 获取 Django 中对象的后代

标签 mysql django django-queryset

我想编写一个方法,将对象的所有后代(即子代、孙子、曾孙...)作为查询集返回。

我写了两个方法,第一个(get_all_children)可以返回子代和孙代,但如果有曾孙(或低代),则会出现以下错误:

django.db.utils.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UNION (SELECT DISTINCT `deneme_app_person`.`id`, `deneme_app_person`.`name`, `de' at line 1")

第二种方法(get_all_children_list)工作得很好(据我测试),但可能更慢,而且最后一行看起来不太好。

models.py

from django.db import models


# Create your models here.
class Person(models.Model):
    name = models.CharField(max_length=100)
    parent = models.ForeignKey(
        'self', null=True, blank=True, related_name='children',
        on_delete=models.CASCADE)

    def get_all_children(self):
        """
        This method will return all children and grandchildren of Person objects
        :return:
        """
        p_list = self.children.all()
        if p_list.count():
            for p in p_list:
                children = p.get_all_children()
                if children.count():
                    p_list = p_list.union(children)
        return p_list.distinct()

    def get_all_children_list(self):
        p_list = self.children.all()
        if p_list.count():
            for p in p_list:
                children = p.get_all_children_list()
                if children.count():
                    p_list = set(p_list).union(children)
                    # p_list = list(set(p_list + children))
        return Person.objects.filter(id__in=[p.id for p in p_list])
        # return list(p_list)

    def __str__(self):
        return f"({self.id}) - {self.name}"

测试.py

from django.test import TestCase
from django.db.models.query import QuerySet

from .models import Person


class PersonTests(TestCase):
    def test_get_all_children(self):
        p = Person.objects.create(name='Jack')
        p1 = Person.objects.create(name='Jack Jr', parent=p)
        p2 = Person.objects.create(name='Jill', parent=p)
        p11 = Person.objects.create(name='Jack Jr2', parent=p1)
        p111 = Person.objects.create(name='Jack Jr3', parent=p11)
        p112 = Person.objects.create(name='Jill Jr', parent=p11)

        # Following test is working
        self.assertEqual([p11, p111, p112], list(p1.get_all_children()))
        # Following test is not working
        self.assertEqual([p1, p2, p11, p111, p112], list(p.get_all_children()))

        for person in [p, p1, p2, p11, p111, p112]:
            self.assertIsInstance(person.get_all_children(), QuerySet)

    def test_get_all_children_list(self):
        p = Person.objects.create(name='Jack')
        p1 = Person.objects.create(name='Jack Jr', parent=p)
        p2 = Person.objects.create(name='Jill', parent=p)
        p11 = Person.objects.create(name='Jack Jr2', parent=p1)
        p111 = Person.objects.create(name='Jack Jr3', parent=p11)
        p112 = Person.objects.create(name='Jill Jr', parent=p11)
        p1111 = Person.objects.create(name='Jack Jr4', parent=p111)

        self.assertEqual([p1111], list(p111.get_all_children_list()))
        self.assertEqual([p111, p112, p1111], list(p11.get_all_children_list()))
        self.assertEqual([p11, p111, p112, p1111], list(p1.get_all_children_list()))
        self.assertEqual([p1, p2, p11, p111, p112, p1111], list(p.get_all_children_list()))

        for person in [p, p1, p2, p11, p111, p112, p1111]:
            self.assertIsInstance(person.get_all_children_list(), QuerySet)

你能指出第一种方法有什么问题吗?如何纠正该方法?

最佳答案

我已经在本地重现了您的错误,但我还没有弄清楚其背后的原因。如果你不介意不使用union,你可以使用|运算符来合并两个查询集,所以你的方法将变成这样:

def get_all_children(self):
    children = self.children.all()
    for child in children:
        children = children | child.get_all_children()

    return children

关于mysql - 获取 Django 中对象的后代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50992400/

相关文章:

mysql - 查询执行时间超过 40 秒

mysql - 编写表约束

python - Django 查询集过滤器 GT、LT、GTE、LTE 返回完整的对象列表

php - 无法更新 MySQL 表

mysql - 在mysql中获取日期的第一周

django - 将参数传递给 PyCharm 自定义管理命令

python - 如何在 django 中加载自定义字段

Django 信用账户示例

mysql - Django 按相关模型字段排序查询集

django - 如何将我的 sqlite 数据传递给 django 并制作图表?