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

83

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

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

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

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

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

Ber
источник

Ответы:

121

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

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

Эли Кортрайт
источник
2
Привет народ, сейчас 2014 год! Примерно через 6 лет библиотеки JS добились огромного прогресса, и фильтрацию не очень больших объемов данных лучше проводить на стороне клиента с поддержкой какой-нибудь красивой библиотеки java-скриптов или хотя бы AJAX-ed.
andilabs
1
@andi: Я, конечно, согласен даже с умеренно большими наборами данных, например, даже с тысячами строк в таблице. После работы с базами данных с миллионами строк, определенно есть место для фильтрации на стороне сервера :)
Эли Кортрайт
конечно, но я просто хотел указать на всех, что люди часто имеют дело с несколькими К строками, что приятный опыт взаимодействия для пользователя может произойти в его браузере. И для людей, имеющих дело даже с огромными наборами данных, хорошим решением может быть некоторый гибридный подход, например, фильтрация с точки зрения от нескольких M до нескольких K на стороне сервера, а другие более легкие сотрудники внутри этих нескольких K делают на стороне клиента.
andilabs
9
@andi За исключением ситуаций, когда вы фильтруете контент на основе разрешений, которые никогда не будут выполняться на стороне клиента. Правильно?
40

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

@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 %}
тобыч
источник
Я пытаюсь это решение , но он все время вызывая ошибку: 'for' statements should use the format 'for x in y': for p in r | people_in_roll_department:d. Есть идеи?
diosney
@diosney, возможно, вы добавите ".all" в предложение "вещи". Это должно быть «things.all»
Энрик Миеза
12

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

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

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

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

Питер Роуэлл
источник
9

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

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

mrmagooey
источник
9

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

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)
chrisv
источник
4
assignment_tag был удален в Django 2.0
Андреас Бергстрём
1

Для всех, кто ищет ответ в 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

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

Кшиштоф Шумко
источник