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


83

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

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

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

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

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

Ответы:


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


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)

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.