Как выполнить фильтрацию запросов в шаблонах django

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

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

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

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

но я просто не могу понять, как писать FILTER.

Ответ 1

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

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

Ответ 2

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

@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 %}

Ответ 3

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

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

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

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

Ответ 4

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

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)

Ответ 5

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

Хорошим примером этого является модель 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, и поэтому не так гибко некоторые из описанных выше методов.