2008-10-21 11 views
62

J'ai besoin d'effectuer une requête filtrée à partir d'un modèle de django, pour obtenir un ensemble d'objets équivalents au code python dans une vue:Comment puis-je effectuer le filtrage des requêtes dans les modèles de django

queryset = Modelclass.objects.filter(somekey=foo) 

Dans mon modèle I aimerait faire

{% for object in data.somekey_set.FILTER %} 

mais je ne peux pas sembler savoir comment écrire FILTER.

Répondre

98

Vous ne pouvez pas faire cela, ce qui est voulu. Les auteurs du framework Django ont voulu une séparation stricte entre le code de présentation et la logique de données. Le filtrage des modèles est une logique de données et la sortie du code HTML est une logique de présentation.

Vous avez donc plusieurs options. Le plus simple est de faire le filtrage, puis passez le résultat à render_to_response. Ou vous pouvez écrire une méthode dans votre modèle afin que vous puissiez dire {% for object in data.filtered_set %}. Enfin, vous pouvez écrire votre propre balise template, bien que dans ce cas précis je vous déconseille de le faire.

+2

Merci pour la clarification du concept de design de django. J'utilise l'approche de la méthode du modèle. – Ber

+2

Bonjour les gens est 2014 maintenant! Environ 6 ans plus tard, les bibliothèques JS ont fait d'énormes progrès, et le filtrage d'une quantité de données pas extrêmement importante devrait plutôt être fait du côté client avec le support d'une belle bibliothèque de scripts java, ou au moins AJAX-ed. – andi

+1

@andi: Je suis certainement d'accord pour des ensembles de données, même modérément grands, par exemple. même des milliers de lignes dans une table. Ayant travaillé sur des bases de données avec des millions de lignes, il y a toujours une place pour le filtrage côté serveur :) –

11

Je rencontre régulièrement ce problème et utilise souvent la solution "ajouter une méthode". Cependant, il y a certainement des cas où «ajouter une méthode» ou «le calculer dans la vue» ne fonctionne pas (ou ne fonctionne pas bien). Par exemple. lorsque vous mettez en cache des fragments de gabarit et que vous avez besoin d'un calcul de DB non trivial pour le produire. Vous ne voulez pas faire le travail de DB sauf si vous en avez besoin, mais vous ne saurez pas si vous en avez besoin jusqu'à ce que vous soyez dans la logique du template.

D'autres solutions possibles:

  1. Utilisez la balise de modèle {% expr < expression> comme < var_name>%} trouvé à http://www.djangosnippets.org/snippets/9/ L'expression est une expression Python juridique avec le contexte de votre modèle en tant que votre portée locale .

  2. Modifiez votre processeur de modèle. Jinja2 (http://jinja.pocoo.org/2/) a une syntaxe qui est presque identique au langage de modèle Django, mais avec une puissance Python complète disponible. C'est aussi plus rapide. Vous pouvez le faire en gros, ou vous pouvez limiter son utilisation aux modèles sur lesquels travaillent, mais utilisez les modèles «plus sûrs» de Django pour les pages maintenues par le concepteur.

6

L'autre option est que si vous avez un filtre que vous voulez toujours appliquée, d'ajouter un custom manager sur le modèle en question qui applique toujours le filtre aux résultats retournés.

Un bon exemple est un modèle Event, où 90% des requêtes que vous faites sur le modèle que vous allez vouloir quelque chose comme Event.objects.filter(date__gte=now), à savoir que vous êtes normalement intéressé par Events qui sont à venir. Cela ressemblerait à ceci:

class EventManager(models.Manager): 
    def get_query_set(self): 
     now = datetime.now() 
     return super(EventManager,self).get_query_set().filter(date__gte=now) 

Et dans le modèle:

class Event(models.Model): 
    ... 
    objects = EventManager() 

Mais encore une fois, cela vaut le même filtre contre toutes les requêtes par défaut effectuées sur le modèle Event et est donc pas aussi souple certaines des les techniques décrites ci-dessus.

8

Ceci peut être résolu avec une étiquette d'affectation:

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) 
22

-je ajouter une balise de modèle supplémentaire comme ceci:

@register.filter 
def in_category(things, category): 
    return things.filter(category=category) 

alors je peux faire:

{% for category in categories %} 
    {% for thing in things|in_category:category %} 
    {{ thing }} 
    {% endfor %} 
{% endfor %} 
+0

J'essaye cette solution mais elle continue à déclencher une erreur: les instructions 'for 'doivent utiliser le format' for x in y ': pour p in r | people_in_roll_department: d'. Des idées? – diosney

+1

Frappé par un vilain bug :(https://code.djangoproject.com/ticket/19882 – diosney