HTML input text box in Django admin.py

I would like to filter the data in Django (admin.py) with a text message in the HTML input text box. I need to filter companies by city in which they are, and the list of all cities is too long. I would like to replace the list of all cities in the filter with a single text input. I found something similar here http://djangosnippets.org/snippets/2429/ , but there are two problems:

  • the author did not send models.py, so it's hard for me to change the code for my needs (+ no comments)
  • the class UserFieldFilterSpec (relatedFilterSpec) is used: but I need to use AllValuesFilterSpec instead of RelatedFilterSpec (more in the django / contrib / admin / filterspecs.py file) because the list of cities is in the same class as comapny (there shoud by city classes, and they must refer to the company with a foreign key (ManyToMany relationships), but for some reason this should be done this way).

The important part of models.py looks something like this:

class Company(models.Model): title = models.CharField(max_length=150,blank=False) city = models.CharField(max_length=50,blank=True) 

and something from admin.py

 class CatalogAdmin(admin.ModelAdmin): form = CatalogForm list_display = ('title','city') list_filter = ['city',] 

So, I need: 1. instead of the list, od city displays one text input in the Django filter 2. After entering the city name in this text input, the filter data by city (filter request can be sent using the submit button or via javascript).

Thank you all for your messages.

+8
html input filter django admin
source share
3 answers

If someone else needs it. It is a bit hacked in the template, but implemented without a js piece.

filters.py

 from django.contrib.admin import ListFilter 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 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 }, ) 

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

 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,] 

A ready-to-use filter will look like this.

+12
source share

While this is not your question, it sounds like the perfect solution for Django-Selectables, you can add AJAX-powered CharField Form with just a few lines, which will have entries selected from the list of cities. Take a look at the samples listed in the link above.

+2
source share

I am running Django 1.10, 1.11 and r_black the solution did not fit completely, since Django was complaining that the filter fields should inherit from "FieldListFilter".

So, a simple change for the filter inherited from FieldListFilter took care of Django's salary and did not require specifying a new class for each field 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 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 (no change):

 {% 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 %} 

Using:

 class MyAdmin(admin.ModelAdmin): list_display = [your fields] list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields] 
0
source share

All Articles