Un acertijo sobre objetos Q y claves foráneas
-
06-07-2019 - |
Pregunta
Tengo un modelo como este:
class Thing(models.Model):
property1 = models.IntegerField()
property2 = models.IntegerField()
property3 = models.IntegerField()
class Subthing(models.Model):
subproperty = models.IntegerField()
thing = modelsForeignkey(Thing)
main = models.BooleanField()
Tengo una función a la que se le pasa una lista de filtros donde cada filtro tiene la forma {'tipo': algo, 'valor': x}. Esta función necesita devolver un conjunto de resultados Y unir todos los filtros:
final_q = Q()
for filter in filters:
q = None
if filter['type'] =='thing-property1':
q = Q(property1=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property2=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property3=filter['value'])
if q:
final_q = final_q & q
return Thing.objects.filter(final_q).distinct()
Cada Subthing tiene una propiedad booleana 'main'. Cada Cosa tiene 1 y solo 1 Subthing donde main == True.
Ahora necesito agregar un filtro que devuelva todas las Cosas que tienen un Subthing donde main == True
y subproperty == filter ['value']
¿Puedo hacer esto como parte del objeto Q
que estoy construyendo? Si no, ¿de qué otra manera? El conjunto de consultas que obtengo antes de mi nuevo filtro puede ser bastante grande, por lo que me gustaría un método que no implique bucle sobre los resultados.
Solución
Es un poco más fácil de entender si explícitamente le das a tus Subthings un " related_name " en su relación con la Cosa
class Subthing(models.Model):
...
thing = models.ForeignKey(Thing, related_name='subthings')
...
Ahora, usa Únete a Django sintaxis para construir su objeto Q:
Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])
La relación inversa tiene el nombre predeterminado 'subthing_set', pero creo que es más fácil de seguir si le da un nombre mejor como 'subthings'.
Otros consejos
Uso (en lugar de final_q = Q ()
al principio)
final_q=Q(subthing_set__main=True)
sub_vals = map(lambda v: v['value'], filters)
if sub_vals:
final_q = final_q & Q(subthing_set__subproperty__in=sub_vals)
debería darte lo que quieres, también puedes ajustar tu ciclo para construir la lista de sub_valos y aplicarlo después del ciclo.
subthing_set es y se agrega automáticamente el campo relacionado agregado a la Cosa para acceder a Subthings relacionados.
puede asignar otro nombre relacionado, por ejemplo,
thing=models.ForeignKey(Thing,related_name='subthings')