web-dev-qa-db-fra.com

Comment puis-je effectuer le filtrage des requêtes dans les modèles Django

J'ai besoin d'effectuer une requête filtrée à partir d'un modèle Django, pour obtenir un ensemble d'objets équivalent à python code dans une vue:

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

Dans mon modèle je voudrais faire

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

mais je n'arrive pas à trouver comment écrire FILTER.

73
Ber

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

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

112
Eli Courtwright

Je viens d'ajouter une balise de modèle supplémentaire comme celle-ci:

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

Ensuite, je peux faire:

{% for category in categories %}
  {% for thing in things|in_category:category %}
    {{ thing }}
  {% endfor %}
{% endfor %}
29
tobych

Je rencontre régulièrement ce problème et j'utilise souvent la solution "ajouter une méthode". Cependant, il y a certainement des cas où "ajouter une méthode" ou "la calculer dans la vue" ne fonctionnent pas (ou ne fonctionnent pas bien). Par exemple. lorsque vous mettez en cache des fragments de modèle et que vous avez besoin d'un calcul DB non trivial pour le produire. Vous ne voulez pas faire le travail de base de données sauf si vous en avez besoin, mais vous ne saurez pas si vous devez le faire tant que vous n'êtes pas plongé dans la logique du modèle.

Quelques autres solutions possibles:

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

  2. Changez votre processeur de modèle. Jinja2 ( http://jinja.pocoo.org/2/ ) a une syntaxe presque identique au Django langage de modèle, mais avec un Python puissance disponible. C'est aussi plus rapide. Vous pouvez le faire en gros, ou vous pouvez limiter son utilisation aux modèles sur lesquels vous travaillez , mais utilisez les modèles "plus sûrs" de Django pour les pages gérées par le concepteur.

11
Peter Rowell

Cela peut être résolu avec une balise 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)
9
chrisv

L'autre option est que si vous avez un filtre que vous souhaitez toujours appliquer, ajouter un gestionnaire personnalisé sur le modèle en question qui applique toujours le filtre aux résultats renvoyés.

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

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 applique le même filtre à toutes les requêtes par défaut effectuées sur le modèle Event et n'est donc pas aussi flexible que certaines des techniques décrites ci-dessus.

8
mrmagooey