Come eseguo il filtro delle query nei modelli di django
-
03-07-2019 - |
Domanda
Devo eseguire una query filtrata all'interno di un modello django, per ottenere una serie di oggetti equivalenti al codice Python in una vista:
queryset = Modelclass.objects.filter(somekey=foo)
Nel mio modello vorrei fare
{% for object in data.somekey_set.FILTER %}
ma non riesco proprio a scoprire come scrivere FILTER.
Soluzione
Non puoi farlo, che è di progettazione. Gli autori del framework Django intendevano una rigorosa separazione del codice di presentazione dalla logica dei dati. Il filtro dei modelli è la logica dei dati e l'output dell'HTML è la logica di presentazione.
Quindi hai diverse opzioni. Il più semplice è fare il filtro, quindi passare il risultato a render_to_response
. Oppure potresti scrivere un metodo nel tuo modello in modo da poter dire {% for object in data.filtered_set %}
. Infine, potresti scrivere il tuo tag modello, anche se in questo caso specifico lo sconsiglio.
Altri suggerimenti
Aggiungo solo un tag modello aggiuntivo come questo:
@register.filter
def in_category(things, category):
return things.filter(category=category)
Quindi posso fare:
{% for category in categories %}
{% for thing in things|in_category:category %}
{{ thing }}
{% endfor %}
{% endfor %}
Mi imbatto in questo problema su base regolare e utilizzo spesso il " aggiungi un metodo " soluzione. Tuttavia, ci sono sicuramente casi in cui & Quot; aggiungi un metodo & Quot; oppure " calcolarlo nella vista " non funziona (o non funziona bene). Per esempio. quando si memorizzano nella cache frammenti di modello e sono necessari alcuni calcoli DB non banali per produrlo. Non vuoi far funzionare il DB a meno che non sia necessario, ma non saprai se devi farlo fino a quando non sarai profondamente nella logica del modello.
Alcune altre possibili soluzioni:
-
Usa {% expr < espressione > come < var_name > %} tag modello trovato in http://www.djangosnippets.org/snippets/9/ L'espressione è qualsiasi espressione Python legale con il contesto del modello come ambito locale.
-
Cambia il tuo processore modello. Jinja2 ( http://jinja.pocoo.org/2/ ) ha una sintassi quasi identica a il linguaggio modello Django, ma con la piena potenza di Python disponibile. È anche più veloce. Puoi farlo all'ingrosso, o potresti limitarne l'uso ai modelli su cui stai , ma usa il & Quot di Django; più sicuro & Quot; modelli per pagine gestite da designer.
Questo può essere risolto con un tag di assegnazione:
from django import template
register = template.Library()
@register.assignment_tag
def query(qs, **kwargs):
""" template tag which allows queryset filtering. Usage:
{% query books author=author as mybooks %}
{% for book in mybooks %}
...
{% endfor %}
"""
return qs.filter(**kwargs)
L'altra opzione è che se hai un filtro che vuoi sempre applicare, aggiungere un gestore personalizzato sul modello in questione che applica sempre il filtro ai risultati restituiti.
Un buon esempio di questo è un Event
modello, in cui per il 90% delle query che fai sul modello vorrai qualcosa come Event.objects.filter(date__gte=now)
, cioè normalmente sei interessato a Events
che sono imminente. Questo sarebbe simile:
class EventManager(models.Manager):
def get_query_set(self):
now = datetime.now()
return super(EventManager,self).get_query_set().filter(date__gte=now)
E nel modello:
class Event(models.Model):
...
objects = EventManager()
Ma ancora una volta, questo applica lo stesso filtro a tutte le query predefinite fatte sul modello <=> e quindi non è così flessibile alcune delle tecniche sopra descritte.