我想写一个方法来返回对象的所有后代(即子代,孙代,曾孙...)作为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}"
tests.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)
您能指出第一种方法有什么问题吗?如何纠正该方法?
答案 0 :(得分:1)
我在本地重现了您的错误,但我还没有弄清楚其背后的原因。如果您不介意不使用union
,则可以使用|
运算符合并两个查询集,因此您的方法将如下所示:
def get_all_children(self):
children = self.children.all()
for child in children:
children = children | child.get_all_children()
return children