Ein Puzzle über Q-Objekte und Fremdschlüssel
-
06-07-2019 - |
Frage
Ich habe ein Modell wie diese bekommen:
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()
Ich habe eine Funktion habe, die eine Liste von Filtern geleitet wird, wo jeder Filter der Form ist { ‚Typ‘: etwas, ‚Wert‘: x}. Diese Funktion benötigt eine Reihe von Ergebnissen zurückzukehren ANDing alle Filter zusammen:
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()
Jeder Subthing hat eine Boolesche Eigenschaft 'main'. Jedes Ding hat 1 und nur 1 Subthing wo Haupt == True fest.
Ich brauche jetzt Filter hinzufügen, die alle Sachen gibt, die eine Subthing haben, wo main==True
und subproperty==filter['value']
Kann ich das als Teil des Q
Objekt Ich bin konstruieren? Wenn nicht, wie sonst? Die queryset ich vor meinem neuen Filter bekommen kann ziemlich groß sein, damit ich eine Methode möchte, die nicht über die Ergebnisse looping beinhaltet.
Lösung
Es ist ein bisschen leichter zu verstehen, wenn Sie ausdrücklich Ihr Subthings eine „related_name“ in ihrer Beziehung zur Sache geben
class Subthing(models.Model):
...
thing = models.ForeignKey(Thing, related_name='subthings')
...
Jetzt verwenden Sie Django beitreten Syntax Ihr Q-Objekt zu erstellen:
Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])
Die umgekehrte Beziehung hat den Standardnamen ‚subthing_set‘, aber ich finde, dass es einfacher ist, zu folgen, wenn Sie ihm einen besseren Namen wie ‚subthings‘ geben.
Andere Tipps
Mit (statt final_q=Q()
am Anfang)
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)
sollten Sie bekommen, was Sie wollen, können Sie auch Ihre Schleife anpassen, um die sub_vals Liste zu erstellen und sie nach der Schleife anwenden.
subthing_set ist und automatisch hinzugefügt verwandter Bereich zum Dinge hinzugefügt zu verwandten Subthings zuzugreifen.
können Sie einen anderen verwandten Namen zuweisen, z.
thing=models.ForeignKey(Thing,related_name='subthings')