21👍
In case anybody still need this. It is little hackish in template, but implemented without a piece of js.
filters.py
:
from django.contrib.admin import ListFilter
from django.core.exceptions import ImproperlyConfigured
class SingleTextInputFilter(ListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, request, params, model, model_admin):
super(SingleTextInputFilter, self).__init__(
request, params, model, model_admin)
if self.parameter_name is None:
raise ImproperlyConfigured(
"The list filter '%s' does not specify "
"a 'parameter_name'." % self.__class__.__name__)
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html
:
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Then according to your models in admin.py
:
class CatalogCityFilter(SingleTextInputFilter):
title = 'City'
parameter_name = 'city'
def queryset(self, request, queryset):
if self.value():
return queryset.filter(city__iexact=self.value())
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = [CatalogCityFilter,]
Ready to use filter would look like this.
5👍
I’m running Django 1.10, 1.11 and r_black‘s solution didn’t completely fit because Django was complaining that filter fields must inherit from ‘FieldListFilter’.
So a simple change for the filter to inherit from FieldListFilter took care of Django complaining and not having to specify a new class for each field, both at the same time.
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
if self.value():
return queryset.filter(imei__icontains=self.value())
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html (unchanged):
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Usage:
class MyAdmin(admin.ModelAdmin):
list_display = [your fields]
list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields]
- How do I add authentication and endpoint to Django Celery Flower Monitoring?
- Django templates – using block.super in included template fails (exception)
- How to add attributes to option tags?
- DISTINCT ON fields is not supported by this database backend
2👍
While it’s not actually your question, this sounds like a perfect solution for Django-Selectables you can with just a few lines add an AJAX powered CharField Form that will have it’s entries selected from the list of cities. Take a look at the samples listed in the link above.
- Using Django view variables inside templates
- Testing a session variable
- How do I update an already existing row when using ModelForms?
- Enabling Django Admin Filters on Many-to-Many Fields
- Can not use celery delay for saved form: object is not JSON serializable
0👍
Below is the fix for field name..in queryset function
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
variable_column = self.parameter_name
search_type = 'icontains'
filter = variable_column + '__' + search_type
if self.value():
return queryset.filter(**{filter: self.value()})
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': ('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )