Reputation: 57511
I'm working on a ListView
which contains both a filter form and search form as described in In a Django template, how to specify a dictionary key which is itself an attribute?. When I submit a search query, I would like it to retain the existing filter query.
Previously, I implemented this by adding hidden <input>
elements of which the value
was obtained from request.GET
using a custom filter, get
, which calls the QueryDict
's get
method. I found that the problem with this is that for MultipleChoiceField
s, for example, if multiple choices are selected, only one is retained, since the get
method only returns the last value as described in https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.QueryDict.get.
I'm now trying a different approach. Using the custom filter relative_url
,
from django import template
from django.utils.http import urlencode
from django.http import QueryDict
register = template.Library()
@register.simple_tag
def relative_url(field_name, value, query_string=None):
"""
Replace the key 'field_name' in the query_string with the given value.
For example,
relative_url('guide', 1, 'q=Christine') == '?q=Christine&guide=1'
(After https://simpleisbetterthancomplex.com/snippet/2016/08/22/dealing-with-querystring-parameters.html)
"""
url = urlencode({field_name: value})
if query_string:
query_dict = QueryDict(query_string, mutable=True)
query_dict[field_name] = value
url = query_dict.urlencode()
return '?' + url
and the custom filter referer_query
,
from urllib.parse import urlparse
from django import template
register = template.Library()
@register.filter
def referer_query(request):
return urlparse(request.META.get('HTTP_REFERER')).query
I've tried to modify the search form as follows:
{% load relative_url %}
{% load referer_query %}
<form action="{% relative_url "q" search_form.q.value.strip|default:'' request|referer_query %}" 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>
There is some Javascript code which submits the form when you press Enter (without having to press a submit button):
$(document).on('click', '[data-behavior="search-clear"]', function(e) {
$('#search').val('').parents('form').submit();
});
$(function() {
setSearchClearVisibility($('#search'));
});
$(document).on('keydown', '#search', function(e) {
var $el = $(this);
setTimeout(function() {
setSearchClearVisibility($el);
}, 10);
});
function setSearchClearVisibility(el) {
if (el.val() === '') {
$('[data-behavior="search-clear"]').hide();
} else {
$('[data-behavior="search-clear"]').show();
}
}
$(document).on('keydown', '#search', function(e) {
var $el = $(this);
setTimeout(function() {
if (e.keyCode !== 27) return;
$('#search').val('').blur();
setSearchClearVisibility($el);
if ($el.data('query') !== '') {
$('#search').parents('form').submit();
}
}, 10);
});
The problem is that when I try this out by a selecting a filter and entering a search query as illustrated below,
when I drop into the debugger in the view's get
method, I see that request.GET
only contains the search query, not the filter queries, despite that request.META['HTTP_REFERER']
does show the filter queries:
ipdb> request.GET
<QueryDict: {'q': ['Christine']}>
ipdb> request.META.get('HTTP_REFERER')
'http://localhost:8000/dashboard/families?q=&guide=6&guide=4&next_outreach=&vip=&app='
In short, it seems that request.GET
is determined only by the <input>
elements in the form, and not by the form's action
, which contains additional query parameters.
How can I make the request.GET
include the query contained by the HTTP_REFERER
?
(I've noticed that this blog post describes a solution to a similar problem, but the code snippet looks a bit spaghetti-like and I think there must be a simpler way).
Upvotes: 0
Views: 2058