2011-02-01 52 views
5

Tengo un modelo de datos de Django como esto (campos de datos omitidos):Django QuerySets anidados

class Atom(Model): 
    pass 

class State(Model): 
    atom = ForeignKey(Atom) 

class Transition(Model): 
    atom = ForeignKey(Atom) 
    upstate = ForeignKey(State,related_name='uptrans') 
    lostate = ForeignKey(State,related_name='lotrans') 

Cuando consulta, los campos a ser restringido puede estar en uno u otro modelo, por lo que es más fácil de consultar en Transition.objects.filter(...) ya que todos los campos en los otros modelos se pueden alcanzar a través de las claves externas. Llamemos al QuerySet resultante t.

Ahora lo que quiero además es un QuerySet a del modelo Atom que corresponde a t, que se puede hacer como a = t.values('atom').distinct(). Hasta aquí todo bien.

Sin embargo, también quiero cada una de las entradas en a tener uno atributo/campo que contiene el QuerySet de los Estados de este átomo, que refleja todavía los criterios en la selección original t, a través de uno cualquiera de los upstate o lostate ForeignKeys.

He creado mi QuerySet de los Estados hasta ahora por más de un bucle t, añadiendo el values('upstate_id') y values('lostate_id') a una pitón set() para tirar duplicados, y luego consultar Unidos con esta lista. Pero entonces no puedo lograr la estructura anidada de Estados dentro de los Átomos.

Cualquier sugerencia sobre cómo hacerlo es bienvenida, si es posible con QuerySet s no evaluado, ya que los paso no a una plantilla sino a un generador (yield declaraciones), que es una buena forma de transmitir grandes cantidades de datos.

Respuesta

2

Creo que la siguiente función hace lo que describo anteriormente, pero no estoy seguro si el ciclo con más filtros del QuerySet original por átomo es el enfoque correcto.

def getAtomsWithStates(t): 
    atom_ids = set(t.values_list('atom_id',flat=True)) 
    atoms = Atoms.objects.filter(pk__in=atom_ids) 
    for atom in atoms: 
     upstate_ids = t.filter(atom=atom).values_list('upstate_id',flat=True) 
     lostate_ids = t.filter(atom=atom).values_list('lostate_id',flat=True) 
     all_ids = set(upstate_ids + lostate_ids) 

     # attach the new QuerySet to the entry in the outer one: 
     atom.States = State.objects.filter(pk__in=all_ids) 

    return atoms 

ahora que puedo hacer el bucle anidado que necesito así:

someAtoms = getAtomsWithStates(Transition.objects.filter(...)) 
for atom in someAtoms: 
    for state in atom.States: 
     print state.field 

Pero, una vez más, puede haber una solución más inteligente para esto y seguramente estaría interesado.

0

Es muy bueno que tenga la comprensión de set s. Sin embargo, el uso de SQL In no duplicará sus datos. Consideremos eso por un momento. Si digo: "Dame un átomo que esté en esta lista: (1, 2, 3, 3, 3, 4)", la base de datos devolverá los átomos 1, 2, 3 y 4. Para simplificar, no lo haría. pídale a Python que realice la aritmética set ya que la base de datos debería poder manejarlo bien. Hay veces para usar set, pero su escenario no parece ser uno de ellos.

Una alternativa para usted:

states = State.objects.filter(
    Q(pk__in=t.values_list('upstate', flat=True)) | 
    Q(pk__in=t.values_list('lostate', flat=True) 
) 

Aún así, parece que su modelo podría utilizar algunos cambios, pero no entiendo completamente lo que estamos tratando de lograr. Observe cómo eso en mi alternativa, no hago nada con los átomos. Uso el objeto Q para poder realizar una operación OR, pero es posible que pueda agregar un indicador al modelo de estado para indicar si es alto o bajo. O puede usar una relación M2M con una tabla directa. ¿Y por qué tu transición y tu estado estarían asociados con un átomo?Se podía eliminar atom de Transition y obtener el atom de State así:

atoms = Atom.objects.filter(
    pk__in=State.objects.filter(
     Q(pk__in=t.values_list('upstate', flat=True)) | 
     Q(pk__in=t.values_list('lostate', flat=True) 
    ).values_list('atom', flat=True) 
)