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

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

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

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

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

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


person Ber    schedule 21.10.2008    source источник


Ответы (6)


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

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

person Eli Courtwright    schedule 22.10.2008
comment
Привет, люди, сейчас 2014 год! Примерно через 6 лет библиотеки JS добились огромного прогресса, и фильтрация небольшого количества данных должна выполняться на стороне клиента с поддержкой какой-либо хорошей библиотеки сценариев java или, по крайней мере, с использованием AJAX. - person andilabs; 12.05.2014
comment
@andi: я, конечно, согласен даже для умеренно больших наборов данных, например. даже тысячи строк в таблице. Поработав с базами данных с миллионами строк, определенно есть место для фильтрации на стороне сервера :) - person Eli Courtwright; 12.05.2014
comment
конечно, но я просто хотел указать всем, что люди, имеющие дело с несколькими тысячами строк, могут получить приятный опыт взаимодействия для пользователя в своем браузере. И для людей, даже имеющих дело с огромными наборами данных, некоторый гибридный подход может быть хорошим решением, например, фильтровать с точки зрения нескольких M до нескольких K на стороне сервера, а другой более легкий персонал внутри этих нескольких K делает на стороне клиента. - person andilabs; 12.05.2014
comment
@andi За исключением ситуаций, когда вы фильтруете контент на основе разрешений, которые никогда не выполняются на стороне клиента. Верно? - person ; 11.08.2014
comment
Фильтрация на стороне сервера является распространенным и безопасным случаем, хотя иногда фильтрацию на стороне клиента можно иногда выбирать, чтобы избежать избыточных обращений к серверу для многократной фильтрации одного и того же ограниченного числа записей. Я не знаю, почему некоторые поклонники библиотек JS предпочитают делать много на стороне клиента. Я видел некоторые панели инструментов на основе js, которые невероятно хорошо разряжают батарею вашего ноутбука. - person Mohammed Shareef C; 05.03.2021

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

@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 %}
person tobych    schedule 07.05.2013
comment
Я пробую это решение, но оно продолжает вызывать ошибку: 'for' statements should use the format 'for x in y': for p in r | people_in_roll_department:d. Любые идеи? - person Diosney; 11.01.2015
comment
@diosney, ты, вероятно, добавишь .all в предложение о вещах. Это должно быть все. - person Enric Mieza; 02.12.2020

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

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

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

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

person Peter Rowell    schedule 23.10.2008

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

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

person mrmagooey    schedule 10.09.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)

РЕДАКТИРОВАТЬ: в Django 2.0 был удален тегassign_tag, это больше не будет работать.

person chrisv    schedule 23.12.2012
comment
Назначение_тег было удалено в Django 2.0. - person Andreas Bergström; 08.05.2018

Для тех, кто ищет ответ в 2020 году. Это сработало для меня.

В представлениях:

 class InstancesView(generic.ListView):
        model = AlarmInstance
        context_object_name = 'settings_context'
        queryset = Group.objects.all()
        template_name = 'insta_list.html'

        @register.filter
        def filter_unknown(self, aVal):
            result = aVal.filter(is_known=False)
            return result

        @register.filter
        def filter_known(self, aVal):
            result = aVal.filter(is_known=True)
            return result

В шаблоне:

{% for instance in alarm.qar_alarm_instances|filter_unknown:alarm.qar_alarm_instances %}

В псевдокоде:

For each in model.child_object|view_filter:filter_arg

Надеюсь, это поможет.

person Krzysztof Szumko    schedule 30.04.2020
comment
Хотя это сработает, считается лучшей практикой создавать фильтры как функции, а не методы класса представления. поместите их в свой собственный файл для лучшей ремонтопригодности, а не для потери их в представлениях. Также позволит улучшить модульное тестирование. - person Llanilek; 12.04.2021