Как выполнить фильтрацию запросов в шаблонах Django - PullRequest
72 голосов
/ 22 октября 2008

Мне нужно выполнить отфильтрованный запрос из шаблона django, чтобы получить набор объектов, эквивалентный коду Python в представлении:

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

В моем шаблоне я бы хотел сделать

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

но я просто не могу понять, как писать ФИЛЬТР.

Ответы [ 5 ]

110 голосов
/ 22 октября 2008

Вы не можете сделать это, что задумано. Авторы инфраструктуры Django намеревались строго отделить код представления от логики данных. Фильтрация моделей - это логика данных, а вывод HTML - это логика представления.

Итак, у вас есть несколько вариантов. Проще всего выполнить фильтрацию, а затем передать результат в render_to_response. Или вы можете написать метод в вашей модели, чтобы вы могли сказать {% for object in data.filtered_set %}. Наконец, вы можете написать свой собственный тег шаблона, хотя в этом конкретном случае я бы посоветовал против этого.

28 голосов
/ 08 мая 2013

Я просто добавляю дополнительный тег шаблона, например:

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

Тогда я могу сделать:

{% for category in categories %}
  {% for thing in things|in_category:category %}
    {{ thing }}
  {% endfor %}
{% endfor %}
11 голосов
/ 23 октября 2008

Я сталкиваюсь с этой проблемой на регулярной основе и часто использую решение "добавить метод". Тем не менее, есть определенно случаи, когда «добавить метод» или «вычислить его в представлении» не работают (или не работают хорошо). Например. когда вы кешируете фрагменты шаблона и вам нужны нетривиальные вычисления БД для его создания. Вы не хотите выполнять работу с БД, если в этом нет необходимости, но вы не будете знать, нужно ли вам это, пока не углубитесь в логику шаблона.

Некоторые другие возможные решения:

  1. Используйте тег шаблона {% expr как %}, найденный в http://www.djangosnippets.org/snippets/9/ Выражение - это любое допустимое выражение Python с контекстом вашего шаблона в качестве локальной области.

  2. Измените ваш шаблонный процессор. Jinja2 (http://jinja.pocoo.org/2/) имеет синтаксис, почти идентичный языку шаблонов Django, но с полной доступной мощью Python. Это также быстрее. Вы можете сделать это оптом или ограничить его использование шаблонами, которые вас работают, но используют более безопасные шаблоны Django для страниц, поддерживаемых дизайнером.

8 голосов
/ 23 декабря 2012

Это можно решить с помощью тега присваивания:

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)
7 голосов
/ 10 сентября 2012

Другой вариант заключается в том, что если у вас есть фильтр, который вы всегда хотите применить, добавьте настраиваемый менеджер к рассматриваемой модели, который всегда применяет фильтр к возвращаемым результатам.

Хорошим примером этого является модель Event, где для 90% запросов, которые вы делаете на модели, вам нужно что-то вроде Event.objects.filter(date__gte=now), т.е. вы обычно заинтересованы в Events, которые предстоящий. Это будет выглядеть так:

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

А в модели:

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

Но, опять же, здесь применяется тот же фильтр ко всем запросам по умолчанию, выполненным на модели Event, и поэтому он не настолько гибок, как некоторые из методов, описанных выше.

...