2011-01-18 3 views
15

У меня есть эта модель с самостоятельной ссылкой внешних ключей соотношения:Django самостоятельно рекурсивный ForeignKey фильтр запрос для все Чайлдса

class Person(TimeStampedModel): 
    name = models.CharField(max_length=32) 
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children') 

Теперь я хочу, чтобы получить все мультиплексные ребенок уровня для человека. Как написать для него запрос Django? Он должен вести себя как рекурсивная функция.

ответ

24

Вы всегда можете добавить рекурсивную функцию для вашей модели:

EDIT: Исправленная в соответствии с SeomGi Han

def get_all_children(self, include_self=True): 
    r = [] 
    if include_self: 
     r.append(self) 
    for c in Person.objects.filter(parent=self): 
     _r = c.get_all_children(include_self=True) 
     if 0 < len(_r): 
      r.extend(_r) 
    return r 

(Не используйте это, если у вас есть много рекурсии или данных ...)

Все еще рекомендую mptt, как предложено errx.

+0

Должен быть get_all_children() в качестве вызова функции, и вам нужно использовать r = r + c.get_all_children(), или вы получите вложенные списки. Кажется, у меня нет прав на редактирование – alan

+0

Обновлен код в соответствии с комментарием. Если всем было разрешено редактировать все сообщения, у нас была бы большая проблема :) – sunn0

+1

Я думаю, что это должно иметь 'include_self = True' во внутреннем вызове функции. В противном случае, при каждой рекурсии мы просто спускаемся на еще один уровень, никогда ничего не добавляя к 'r'. Это только работало правильно для меня после внесения изменений. – andy

12

Вы должны прочитать о обходном дереве с измененным предлогом. Вот реализация django. https://github.com/django-mptt/django-mptt/

+1

является необходимым использовать решение третьей стороны? Есть ли другой путь?? – Ahsan

+2

@ Ашан: Вы всегда можете прочитать об этом, например. здесь: http://dev.mysql.com/tech-resources/articles/hierarchical-data.html и написать код самостоятельно –

5

Если вы знаете максимальную глубину дерева, вы можете попробовать что-то вроде этого (непроверенные): предложение

Person.objects.filter(Q(parent=my_person)|Q(parent__parent=my_person)| Q(parent__parent__parent=my_person)) 
6

sunn0 является отличная идея, но get_all_children() возвращает странные результаты. Он возвращает что-то вроде [Person1, [Person3, Person4], []]. Его следует изменить так, как показано ниже.

def get_all_children(self, include_self=True): 
    r = [] 
    if include_self: 
     r.append(self) 
    for c in Person.objects.filter(parent=self): 
     _r = c.get_all_children(include_self=True) 
     if 0 < len(_r): 
      r.extend(_r) 
    return r 
0

У меня был очень похож business problem, в котором данный член команды, я должен был узнать полную команду под под ним. Но наличие большого числа сотрудников сделало рекурсивное решение очень неэффективным, а также мой API получал ошибки таймаута с сервера.

Принятое решение принимает узел, переходит к его первому ребенку и идет глубоко вниз до нижней части иерархии. Затем снова возвращается к второму ребенку (если существует), а затем снова идет вниз. Короче говоря, он исследует все узлы один за другим и добавляет все члены в массиве. Это приводит к множеству вызовов db, и их следует избегать, если необходимо изучить огромное количество узлов. Решение, с которым я столкнулся, выбирает узлы по-разному. Количество вызовов db равно количеству слоев. Посмотрите на это SO link для решения.

0

Я знаю, что это старый, но кому-то может помочь.

Учитывая экземпляр Person модели, скажем person, вы можете просто сделать:

children = person.children.all() # children being the related_name of the recursive field