Я работаю над проектом Django с ListView
, который имеет как форму поиска, известную в контексте представления как search_form
, так и форму фильтра filter_form
. Интерфейс выглядит так:
И 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» в строке запроса:
Как я понимаю из документов 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
, ожидая целое число в качестве входных данных) :
Почему поиск атрибутов в этом случае не работает?
Обновление 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>
Затем я выбираю фильтр и ввожу поисковый запрос в строку поиска:
Затем, когда я устанавливаю трассировку сразу после метода 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
?