In Django, come si può filtrare un QuerySet con ricerche di campo dinamiche?


161

Dato un corso:

from django.db import models

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

È possibile, e se sì, come avere un QuerySet che filtra in base ad argomenti dinamici? Per esempio:

 # 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`.

Risposte:


311

L'espansione dell'argomento di Python può essere utilizzata per risolvere questo problema:

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

Person.objects.filter(**kwargs)

Questo è un linguaggio Python molto comune e utile.


6
Solo un rapido avvertimento: assicurati che le stringhe nei kwargs siano di tipo str non unicode, altrimenti filter () brontolerà.
Steve Jalim

1
@santiagobasulto Si riferisce anche a un parametro imballaggio / disimballaggio e sue varianti.
Daniel Naab,

7
bello, bello e bello !
Oscar Mederos,

5
@DanielNaab ma questo funzionerà solo su kwargs che lavorano sul filtro delle condizioni AND, qualsiasi alternativa per la condizione OR.
Prateek099,

3
@prateek si può sempre usare Q oggetti: stackoverflow.com/questions/13076822/...
deecodameeko

6

Un esempio semplificato:

In un'app di sondaggio Django, volevo un elenco di selezione HTML che mostra gli utenti registrati. Ma poiché abbiamo 5000 utenti registrati, avevo bisogno di un modo per filtrare quell'elenco in base a criteri di query (come solo le persone che hanno completato un determinato seminario). Affinché l'elemento del sondaggio fosse riutilizzabile, avevo bisogno che la persona che creava la domanda del sondaggio fosse in grado di allegare quei criteri a quella domanda (non voglio codificare la query nell'app).

La soluzione che mi è venuta in mente non è intuitiva al 100% (richiede l'aiuto di un tecnico per creare la query) ma risolve il problema. Durante la creazione della domanda, l'editor può inserire un dizionario in un campo personalizzato, ad esempio:

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

Quella stringa è archiviata nel database. Nel codice vista, ritorna come self.question.custom_query. Il valore è una stringa che assomiglia a un dizionario. Lo trasformiamo in un vero dizionario con eval () e poi lo inseriamo nel queryset con ** kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   

Mi chiedo cosa ci vorrebbe per creare un ModelField / FormField / WidgetField personalizzato che implementasse il comportamento per consentire all'utente, sul lato della GUI, di "costruire" una query, senza mai vedere il testo reale, ma usando un'interfaccia per fare così. Sembra un progetto pulito ...
T. Stone,

1
T. Stone - Immagino che sarebbe facile costruire uno strumento del genere in modo semplicistico se i modelli che necessitano di query fossero semplici, ma molto difficili da fare in un modo completo che esponesse tutte le possibili opzioni, soprattutto se i modelli fossero complesso.
shacker,

5
-1 chiamare eval()l'importazione dell'utente è una cattiva idea, anche se ti fidi completamente dei tuoi utenti. Un campo JSON sarebbe un'idea migliore qui.
John Carter,

5

Django.db.models.Q è esattamente quello che vuoi in un modo Django.


7
Potresti (o qualcuno) fornire un esempio di come usare gli oggetti Q nell'uso dei nomi di campi dinamici?
jackdbernier,

3
È lo stesso della risposta di Daniel Naab. L'unica differenza è che passi gli argomenti nel costruttore di oggetti Q. Q(**filters), se si desidera creare oggetti Q dinamicamente, è possibile inserirli in un elenco e utilizzare .filter(*q_objects)o utilizzare gli operatori bit a bit per combinare gli oggetti Q.
Will S

5
Questa risposta dovrebbe davvero includere un esempio dell'uso di Q per risolvere il problema di OP.
pdoherty926

-2

Un modulo di ricerca davvero complesso di solito indica che un modello più semplice sta cercando di scavare.

Come, esattamente, ti aspetti di ottenere i valori per il nome e l'operazione della colonna? Dove ottieni i valori di 'name'un 'startswith'?

 filter_by = '%s__%s' % ('name', 'startswith')
  1. Un modulo di "ricerca"? Stai per ... cosa? - scegli il nome da un elenco di nomi? Scegli l'operazione da un elenco di operazioni? Mentre a tempo indeterminato, la maggior parte delle persone trova questo confuso e difficile da usare.

    Quante colonne hanno tali filtri? 6? 12? 18?

    • Alcune? Una complessa lista di selezione non ha senso. Alcuni campi e alcune dichiarazioni if ​​hanno senso.
    • Un largo numero? Il tuo modello non suona bene. Sembra che il "campo" sia in realtà una chiave per una riga in un'altra tabella, non una colonna.
  2. Pulsanti filtro specifici. Aspetta ... Funziona così l'amministratore di Django. Filtri specifici vengono trasformati in pulsanti. E si applica la stessa analisi di cui sopra. Alcuni filtri hanno senso. Un gran numero di filtri di solito significa una sorta di prima violazione della forma normale.

Molti campi simili spesso indicano che avrebbero dovuto esserci più righe e meno campi.


9
Con rispetto, è presuntuoso dare consigli senza sapere nulla del design. Per "semplicemente implementare" questa applicazione genererebbe funzioni astronomiche (> 200 app ^ 21 foos) per soddisfare i requisiti. Stai leggendo lo scopo e l'intento nell'esempio; non dovresti. :)
Brian M. Hunt,

2
Incontro molte persone che ritengono che il loro problema sarebbe banale da risolvere se solo le cose fossero (a) più generiche e (b) funzionino come immaginavano. In questo modo risiede la frustrazione infinita perché le cose non sono come immaginavano. Ho visto troppi fallimenti derivare dal "fissaggio del framework".
S. Lott,

2
Le cose funzionano come previsto e desiderato per la risposta di Daniel. La mia domanda riguardava la sintassi, non il design. Se avessi avuto il tempo di scrivere il disegno, l'avrei fatto. Sono sicuro che il tuo contributo sarebbe utile, tuttavia non è solo un'opzione pratica.
Brian M. Hunt,

8
S.Lott, la tua risposta non risponde nemmeno a distanza a questa domanda. Se non conosci una risposta, ti preghiamo di lasciare la domanda da sola. Non rispondere con consigli di progettazione non richiesti quando hai una conoscenza del progetto assolutamente nulla!
serpente,

2
@slypete: se una modifica alla progettazione rimuove il problema, il problema è risolto. Continuare lungo il percorso basato su un design scadente è più costoso e complesso del necessario. Risolvere i problemi alla radice è meglio che risolvere altri problemi che derivano da cattive decisioni di progettazione. Mi dispiace che non ti piaccia l'analisi della causa principale. Ma quando qualcosa è davvero difficile, di solito significa che stai provando la cosa sbagliata per cominciare.
S.Lott
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.