Un casse-tête concernant les objets Q et les clés étrangères
-
06-07-2019 - |
Question
J'ai un modèle comme celui-ci:
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()
J'ai une fonction à laquelle est transmise une liste de filtres dans laquelle chaque filtre est de la forme {'type': quelque chose, 'valeur': x}. Cette fonction doit renvoyer un ensemble de résultats ET tous les filtres ensemble:
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()
Chaque sous-sol a une propriété booléenne 'main'. Chaque chose a 1 et seulement 1 Subthing où main == True.
Je dois maintenant ajouter un filtre qui renvoie toutes les choses qui ont un Subthing où main == True
et sous-propriété == filter ['valeur']
Puis-je faire cela dans le cadre de l'objet Q
que je suis en train de construire? Sinon comment d'autre? Le nombre de requêtes que je reçois avant mon nouveau filtre peut être assez volumineux; j'aimerais donc une méthode qui n'impliquerait pas une boucle sur les résultats.
La solution
C’est un peu plus facile à comprendre si vous donnez explicitement à vos Subthings un "quotient". dans leur relation à la chose
class Subthing(models.Model):
...
thing = models.ForeignKey(Thing, related_name='subthings')
...
Maintenant, vous utilisez rejoindre Django syntaxe pour construire votre objet Q:
Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])
La relation inverse a le nom par défaut 'subthing_set', mais je trouve qu'il est plus facile à suivre si vous lui donnez un meilleur nom comme 'subthings'.
Autres conseils
Utilisation (au lieu de final_q = Q ()
au début)
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)
devrait vous donner ce que vous voulez, vous pouvez également ajuster votre boucle pour construire la liste de sous-valeurs et l'appliquer après la boucle.
subthing_set est et le champ associé a été ajouté automatiquement. Il est ajouté à la rubrique Accès aux sous-objets associés.
vous pouvez attribuer un autre nom associé, par exemple
thing=models.ForeignKey(Thing,related_name='subthings')