Як виконати фільтрацію запитів у шаблонах 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: Я, безумовно, погоджуюсь навіть на помірно великі набори даних, наприклад, навіть на тисячі рядків у таблиці. Попрацювавши над базами даних з мільйонами рядків, все одно точно є місце для фільтрації на стороні сервера :)
Елі Кортрайт

Звичайно, але я просто хотів вказати на те, що люди, які мають справу з часто кількома 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.