Dans Django, comment filtrer un QuerySet avec des recherches de champs dynamiques?

Étant donné une classe:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

Est-il possible, et si oui comment, d'avoir un QuerySet qui filtre en fonction d'arguments dynamiques? Par exemple:

 # Instead of:
 Person.objects.filter(name__startswith='B')
 # ... and:
 Person.objects.filter(name__endswith='B')

 # ... is there some way, given:
 filter_by = '{0}__{1}'.format('name', 'startswith')
 filter_value = 'B'

 # ... that you can run the equivalent of this?
 Person.objects.filter(filter_by=filter_value)
 # ... which will throw an exception, since `filter_by` is not
 # an attribute of `Person`.
128
demandé sur Antti Haapala 2008-11-22 05:06:06

4 réponses

L'expansion des arguments de Python peut être utilisée pour résoudre ce problème:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

C'est un idiome Python très commun et utile.

248
répondu Daniel Naab 2014-02-17 05:57:44

Un exemple simplifié:

Dans une application de sondage Django, je voulais une liste de sélection HTML montrant les utilisateurs enregistrés. Mais parce que nous avons 5000 utilisateurs enregistrés, j'avais besoin d'un moyen de filtrer cette liste en fonction de critères de requête (tels que les personnes qui ont terminé un certain atelier). Pour que l'élément d'enquête soit réutilisable, j'avais besoin que la personne qui créait la question d'enquête puisse attacher ces critères à cette question (Je ne veux pas coder en dur la requête dans l'application).

Le la solution que j'ai trouvée n'est pas 100% conviviale (nécessite l'aide d'une personne technique pour créer la requête) mais elle résout le problème. Lors de la création de la question, l'éditeur peut entrer un dictionnaire dans un champ personnalisé, par exemple:

{'is_staff':True,'last_name__startswith':'A',}

Cette chaîne est stockée dans la base de données. Dans le code de vue, il revient comme self.question.custom_query . La valeur de cela est une chaîne qui ressemble à {[11] } comme un dictionnaire. Nous le transformons en un dictionnaire real avec eval () et le remplissons ensuite dans le queryset avec **kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   
6
répondu shacker 2009-03-18 17:52:22

Django.DB.modèle.Q est exactement ce que vous voulez D'une manière Django.

5
répondu Brent81 2015-01-20 11:15:30

Un formulaire de recherche vraiment complexe indique généralement qu'un modèle plus simple essaie de creuser son chemin.

Comment, exactement, vous attendez-vous à obtenir les valeurs pour le nom de la colonne et l'opération? Où obtenez-vous les valeurs de 'name' un 'startswith'?

 filter_by = '%s__%s' % ('name', 'startswith')
  1. Un formulaire "recherche"? Vous allez -- quoi? -- de choisir le nom à partir d'une liste de noms? Choisissez l'opération dans une liste d'opérations? Bien que ouvert, la plupart des gens trouvent cette confusion et difficile à utiliser.

    Combien les colonnes ont de tels filtres? 6? 12? 18?

    • quelques-uns? Une liste de sélection complexe n'a pas de sens. Quelques champs et quelques si-états sens.
    • Un grand nombre? Votre modèle ne sonne pas bien. On dirait que le "champ" est en fait une clé d'une ligne dans une autre table, pas une colonne.
  2. Boutons de filtre spécifiques. Attendre... C'est ainsi que fonctionne L'administrateur Django. Des filtres spécifiques sont transformés en boutons. Et la même analyse que ci-dessus s'applique. Quelques les filtres ont du sens. Un grand nombre de filtres signifie généralement une sorte de première forme normale violation.

Beaucoup de champs similaires signifie souvent qu'il aurait dû y avoir plus de lignes et moins de champs.

-1
répondu S.Lott 2008-11-22 02:36:56