В шаблоне Django как указать ключ словаря, который сам по себе является атрибутом? - PullRequest
0 голосов
/ 03 мая 2018

Я работаю над проектом Django с ListView, который имеет как форму поиска, известную в контексте представления как search_form, так и форму фильтра filter_form. Интерфейс выглядит так:

enter image description here

И search_form, и filter_form в конечном итоге изменяют то, что возвращается методом ListView get_queryset. Я хотел бы сделать так, чтобы при первом применении фильтра, а затем при поиске он выполнял поиск по отфильтрованным результатам.

На данный момент функция «обратный» уже реализована: когда вы сначала выполняете поиск, а затем фильтруете, он фильтрует результаты поиска. Это из-за скрытого элемента input в filter_form:

<form action={% url 'dashboard:families' %} method="GET" data-behavior="filters">
                <input type="hidden" name="q" value="{{ request.GET.q.strip }}"/>

                <div class="input-field col s2">
                  {{ filter_form.guide }}
                  <label class="active">Guide</label>
                  {% if filter_form.is_guide_filled %}
                    <a href="" class="clear"><i class="material-icons tiny">clear</i></a>
                  {% endif %}
                </div>

Для сравнения, панель поиска имеет следующий шаблон, _search.html:

<form action="{% url action %}" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder="{{ placeholder }}"
        autocomplete="off" type="search" name="q"
        value="{{ search_form.q.value.strip|default:'' }}"
        data-query="{{ search_form.q.value.strip|default:'' }}">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        {% if not search_form.q.value %}style="display: none;"{% endif %}>close</i>
  </div>
</form>

В главном представлении списка, index.html, шаблон поиска включен так:

{% block search_form %}
  {% with action='dashboard:families' placeholder='Search Families' %}
    {% include '_search.html' %}
  {% endwith %}
{% endblock %}

Чтобы форма поиска сохранила фильтры, я заметил, что только для поля guide работает следующее:

<input type="hidden" name="guide" value="{{ request.GET.guide }}"/>

Я хотел бы обобщить это, чтобы включить все фильтры. Я пробовал следующее:

  {% if filter_form %}
    {% for field in filter_form %}
      {% with field_name=field.name %}
        <input type="hidden" name=field_name value="{{ request.GET.field_name}}"/>
      {% endwith %}
    {% endfor %}
  {% endif %}

Однако, если я попробую это, я буквально получу «field_name» в строке запроса:

enter image description here

Как я понимаю из документов DTL , точечная нотация реализует поиск по словарю, поиск по атрибутам и поиск по индексу списка. Если бы я попробовал что-то вроде

request.GET.field.name

Вероятно, он попытается найти «поле» в словарном объекте request.GET и ничего не найти. В обычном Python я бы хотел сделать

request.GET[field.name]

Я думал, что смогу сделать это с помощью блока with, но, видимо, это не сработает. Любой совет, как это реализовать?

Обновление

Если я укажу атрибут name для элемента input как "{{field_name}}" вместо просто field_name, например,

<input type="hidden" name="{{field_name}}" value="{{ request.GET.field_name}}"/>

Проблема в том, что value устанавливается в пустую строку вместо желаемого значения, что приводит к ValueError для поля guide (то есть ModelChoiceField, ожидая целое число в качестве входных данных) :

enter image description here

Почему поиск атрибутов в этом случае не работает?

Обновление 2

Отвечая на комментарий Лемейзера, полный ответ:

Traceback:

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/two_factor/views/mixins.py" in dispatch
  82.         return super(OTPRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  56.         return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  92.         return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py" in get
  74.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py" in get
  111.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/base.py" in get
  74.         return super().get(request, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/views/generic/list.py" in get
  160.         self.object_list = self.get_queryset()

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py" in get_queryset
  122.             queryset = queryset.filter(lucy_guide__in=guide)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py" in filter
  784.         return self._filter_or_exclude(False, *args, **kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
  802.             clone.query.add_q(Q(*args, **kwargs))

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_q
  1250.         clause, _ = self._add_q(q_object, self.used_aliases)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1276.                     allow_joins=allow_joins, split_subq=split_subq,

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_filter
  1206.             condition = lookup_class(lhs, value)

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/lookups.py" in __init__
  24.         self.rhs = self.get_prep_lookup()

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in get_prep_lookup
  56.                 self.rhs = [target_field.get_prep_value(v) for v in self.rhs]

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in <listcomp>
  56.                 self.rhs = [target_field.get_prep_value(v) for v in self.rhs]

File "/Users/kurtpeek/Documents/Dev/lucy/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py" in get_prep_value
  966.         return int(value)

Exception Type: ValueError at /dashboard/families
Exception Value: invalid literal for int() with base 10: ''

Если я установлю трассировку в методе get_queryset() представления, непосредственно перед 122 в трассировке стека, я вижу, что guide действительно пустой список, содержащий пустую строку, как это есть в self.request.GET:

> /Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/views/families.py(121)get_queryset()
    120 
--> 121         if guide:
    122             queryset = queryset.filter(lucy_guide__in=guide)

ipdb> guide
['']

ipdb> self.request.GET
<QueryDict: {'q': ['Christine'], 'status': [''], 'next_outreach': [''], 'country': [''], 'vip': [''], 'app': [''], 'guide': [''], 'package': ['']}>

Помимо трудностей с отправкой пустого значения, я действительно ожидал бы, что поле guide в request.GET будет иметь непустое значение здесь. Например, если я вернусь к «упрощенной» версии скрытой формы фильтра, только с полем guide:

<form action="{% url action %}" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder="{{ placeholder }}"
        autocomplete="off" type="search" name="q"
        value="{{ search_form.q.value.strip|default:'' }}"
        data-query="{{ search_form.q.value.strip|default:'' }}">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        {% if not search_form.q.value %}style="display: none;"{% endif %}>close</i>
  </div>
  <input type="hidden" name="guide" value="{{ request.GET.guide }}"/>
</form>

Затем я выбираю фильтр и ввожу поисковый запрос в строку поиска:

enter image description here

Затем, когда я устанавливаю трассировку сразу после метода get представления, примерно так:

def get(self, request, *args, **kwargs):
    import ipdb; ipdb.set_trace()

Я вижу, что и q, и guide находятся в request.GET:

ipdb> request.GET
<QueryDict: {'q': ['Christine'], 'guide': ['6']}>

Так что в «общей» форме с request.GET.field_name я бы также ожидал, что request.GET будет выглядеть так же, как и пустые списки для других полей. Кажется, что точечная нотация языка шаблонов Django пытается буквально выполнить поиск по словарю или атрибуту для 'field_name' и не находит ничего; возможно, мне следует написать собственный фильтр, как описано в шаблоне Django, как искать значение словаря с переменной , чтобы выполнить поиск по словарю field.name?

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Я наконец решил эту проблему, написав собственный фильтр get, как описано в Шаблон Django, как искать значение словаря с переменной :

from django import template

register = template.Library()


@register.filter
def get(dictionary, key):
    return dictionary.get(key)

Я обновил _search.html следующим образом:

{% load get %}

<form action="{% url action %}" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder="{{ placeholder }}"
        autocomplete="off" type="search" name="q"
        value="{{ search_form.q.value.strip|default:'' }}"
        data-query="{{ search_form.q.value.strip|default:'' }}">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        {% if not search_form.q.value %}style="display: none;"{% endif %}>close</i>
  </div>
  {% if filter_form %}
    {% for field in filter_form %}
      <input type="hidden" name="{{ field.name }}" value="{{ request.GET|get:field.name }}"/>
    {% endfor %}
  {% endif %}
</form>

Теперь, если я пытаюсь найти отфильтрованный результат, он работает как положено:

enter image description here

Обратите внимание, что это также хорошо работает для фильтров, которые не применяются - они имеют значение None вместо пустой строки - без необходимости отфильтровывать их в форме.

0 голосов
/ 03 мая 2018

Таким образом, это сводится к тому, что не передает ключи в запросе GET с пустым строковым значением . Это кажется неподдерживаемым в HTML; вам нужно немного магии JS, чтобы это произошло. Смотрите эту ветку: Как запретить отправку значения поля ввода формы HTML, если оно пустое

Тем не менее, чисто решение Django состояло бы в том, чтобы изменить ваш фильтр dict, чтобы исключить ключи, которые являются нулевыми. Я не уверен, как вы фильтруете это в Django, но при условии, что вы переопределили метод get_queryset; Вы всегда можете сделать:

def get_queryset(self):
    qs = super(YourView, self).get_queryset()
    filters = {k, v for k, v in request.GET.items() if v != ''}  # Be as generic/specific as needed here for exclusion
    qs = qs.filter(**filters)  # Fire your filtering logic here; this is a sample
    return qs
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...