79 votes

Django, filtrage de requête à partir de la méthode du modèle

J'ai ces modèles :

def Foo(Models.model):
    size = models.IntegerField()
    # autres champs

    def is_active(self):
         if check_condition:
              return True
         else:
              return False

def Bar(Models.model):
     foo = models.ForeignKey("Foo")
     # autres champs

Maintenant je veux interroger les Bars qui ont des Foo actifs comme ceci :

Bar.objects.filter(foo.is_active())

Je reçois des erreurs telles que

SyntaxError at /
('non-keyword arg after keyword arg'

Comment puis-je réaliser cela ?

45voto

Ignacio Vazquez-Abrams Points 312628

Vous ne pouvez pas interroger les méthodes ou propriétés du modèle. Utilisez soit les critères à l'intérieur de celui-ci dans la requête, soit filtrez en Python en utilisant une compréhension de liste ou un genex.

8 votes

Ou stocker la valeur par laquelle vous souhaitez filtrer dans un champ de base de données et la mettre à jour lorsque vous en avez besoin (généralement lors de l'enregistrement). Ensuite, vous pouvez filtrer/triez-le exactement comme tout autre champ.

6 votes

Si je réutilise les critères dans la méthode de requête, comment puis-je rester DRY ? Mes méthodes sont extrêmement importantes pour notre logique métier et si ladite logique change, je ne peux pas me permettre que mes méthodes se cassent. Rester DRY est extrêmement important.

33voto

0sh Points 601

Vous pouvez également utiliser un gestionnaire personnalisé. Ensuite, vous pourriez exécuter quelque chose comme ceci :

Bar.objects.foo_active()

Et tout ce que vous avez à faire est :

class BarManager(models.Manager):
    def foo_active(self):
       # utilisez votre méthode pour filtrer les résultats
       return you_custom_queryset

Consultez la documentation.

1 votes

Je souligne que cette approche empêcherait le chaînage des requêtes : il ne serait pas possible d'exécuter du code Python dans le gestionnaire personnalisé puis d'ajouter une méthode de requête, telle que filter ou exclude. La raison en est que Django évaluera la requête lors de l'exécution d'une méthode non requête. La requête se compose de commandes SQL qui récupéreront les données interrogées de la base de données. Dans cet exemple, si Django n'évaluait pas la requête pour récupérer les données de la base de données, la méthode Python dans ce gestionnaire personnalisé n'aurait aucune donnée avec laquelle interagir.

1 votes

@TD1 C'est la remarque précieuse. Techniquement, je dirais que cette méthode de gestion personnalisée ne vous empêche pas d'enchaîner les requêtes car vous pouvez toujours l'utiliser comme une méthode terminale dans la chaîne. Mais vous devez garder cela à l'esprit car cela pourrait être sujet aux erreurs.

26voto

SummerBreeze Points 656

J'ai eu un problème similaire : j'utilise la vue basée sur une classe object_list et j'ai dû filtrer par méthode du modèle. (stocker l'information en base de données n'était pas une option car la propriété était basée sur le temps et je devrais créer un cronjob et/ou... pas possible)

Ma réponse n'est pas efficace et je ne sais pas comment elle va s'adapter à des données plus grandes ; mais, ça fonctionne :

q = Model.objects.filter(...)...
# voici l'astuce
q_ids = [o.id for o in q if o.method()]
q = q.filter(id__in=q_ids)

7 votes

C'est la seule façon que je vois de rester DRY, cependant cela pourrait entraver les performances avec des ensembles de données plus grands (n'oubliez pas que les listes sont stockées en mémoire)

10voto

Gabriel Hurley Points 17079

Vous ne pouvez pas filtrer sur les méthodes, cependant si la méthode is_active de Foo vérifie un attribut sur Foo, vous pouvez utiliser la syntaxe du double tiret bas comme Bar.objects.filter(foo__is_active_attribute=True)

0 votes

Je reçois : Impossible de résoudre le mot-clé 'is_active_attribute' en champ Les choix sont : .... tous les noms de variables listés ici

1 votes

Oui, c'est pourquoi j'ai dit "si la méthode is_active de Foo vérifie un attribut...". Le filtrage ne fonctionne que sur les champs de la base de données, pas sur les méthodes. Donc, si is_active vérifie un attribut à l'intérieur de la méthode, vous pouvez vérifier la même chose dans votre filtre. Si cela fait quelque chose de plus compliqué, vous êtes malchanceux et devez trouver une solution différente. Si vous voulez une réponse spécifique, postez le vrai code pour Foo.is_active(). Sinon, tout ce que vous obtiendrez sera des solutions en pseudo-code auxquelles vous ne devriez pas vous attendre à ce qu'elles fonctionnent littéralement.

0voto

Hans Points 36

Apparemment, et comme l'a souligné Ignacio Vazquez-Abrams, vous ne pouvez pas interroger les méthodes du modèle dans une vue. Au lieu de cela, j'appliquerais la logique de filtre requise dans la vue.

Par exemple, lorsque check_condition est une plage de dates :

class BarView(ListView):
    model = Bar
    template_name = "app/bar.html"

    def get_queryset(self):
        # Filtrer pour une condition (ici la condition est une plage de dates)
        now = timezone.now().date()
        return Bar.objects.filter(foo__start_date__lte=now, foo__end_date__gte=now)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X