2011-01-18 17 views
15

tengo este modelo con un autorreferenciado relación de clave externa:Django consulta de filtro ForeignKey auto-recursiva para todos los niño

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

Ahora quiero llegar a todos los niños de múltiples niveles para una persona. ¿Cómo escribo una consulta de Django para ello? Necesita comportarse como una función recursiva.

Respuesta

24

siempre se puede añadir una función recursiva para su modelo:

EDIT: Corregida según lo 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 

(No utilice esta opción si tiene una gran cantidad de datos o recursividad ...)

Siempre recomendando mptt como lo sugiere errx.

+0

Debe ser get_all_children() como la llamada a la función y necesita usar r = r + c.get_all_children() o terminará con listas anidadas. No parece tener los derechos para editar esto yo mismo – alan

+0

Código actualizado de acuerdo al comentario. Si todos pudiéramos editar todas las publicaciones, tendríamos un gran problema :) – sunn0

+1

Creo que esto debería tener 'include_self = True' en la llamada de función interna también. De lo contrario, en cada recursión bajamos un nivel más, nunca añadimos nada a 'r'. Solo funcionó correctamente después de hacer ese cambio. – andy

12

Debería leer sobre Recorrido de árbol de preorden modificado. Aquí está la implementación django. https://github.com/django-mptt/django-mptt/

+1

es necesario el uso de solución de terceros? ¿¿Hay alguna otra manera?? – Ahsan

+2

@ Ashan: siempre puede leer sobre esto, p. aquí: http://dev.mysql.com/tech-resources/articles/hierarchical-data.html y escriba el código usted mismo –

5

Si conoce la profundidad máxima de su árbol, usted podría intentar algo como esto (no probado): sugerencia

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

de sunn0 es gran idea, pero get_all_children() devuelve resultados extraños. Devuelve algo como [Person1, [Person3, Person4], []]. Debería ser cambiado a me gusta a continuación.

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

tuve una muy similar business problem, en el que da un miembro del equipo, que tenía que averiguar el equipo completo bajo sus órdenes. Pero tener una gran cantidad de empleados hizo que la solución recursiva fuera muy ineficiente y también mi API obtenía errores de tiempo de espera del servidor.

La solución aceptada toma el nodo, va a su primer hijo y desciende hasta el final de la jerarquía. Luego regresa nuevamente al segundo niño (si existe), y luego vuelve a bajar hasta el final. En resumen, explora todos los nodos uno por uno y agrega a todos los miembros en una matriz. Esto genera muchas llamadas a bases de datos y debe evitarse si hay una gran cantidad de nodos que explorar. La solución que se me ocurrió fue buscar los nodos en capas. El número de llamadas a bases de datos es igual al número de capas. Eche un vistazo a este SO link para la solución.

0

Sé que esto es viejo, pero alguien podría obtener ayuda.

Dada una instancia del modelo Person, person decir, sólo tiene que hacer:

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