2011-07-03 32 views
8

Me gustaría filtrar los datos en Django (admin.py) con texto escrito en el cuadro de texto de entrada HTML. Necesito filtrar las empresas por ciudad en la que se encuentran y la lista de todas las ciudades es demasiado larga. Me gustaría reemplazar la lista de todas las ciudades en filtrar por una entrada de texto. He encontrado algo similar aquí http://djangosnippets.org/snippets/2429/ pero hay dos problemas:Cuadro de texto de entrada HTML en Django admin.py filtro

  1. autor no publicada models.py, por lo que es difficuilt cambiar el código para mis necesidades (sin comentarios) + clase
  2. se utiliza UserFieldFilterSpec (RelatedFilterSpec): pero necesito usar AllValuesFilterSpec en lugar de RelatedFilterSpec (más en el archivo django/contrib/admin/filterspecs.py), porque la lista de ciudades está en la misma clase que comapny (debe decirse por clase de ciudades y deben ser hacer referencia a la empresa por clave externa (relación ManyToMany), pero por alguna razón debe hacerse de esta manera)

parte importante de models.py ve algo como esto

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

y algo de admin.py

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

Así que de nuevo, necesito: 1. En lugar de la lista de ciudades od mostrar un texto entrada en el filtro Django 2. Después de ingresar el nombre de la ciudad en esa entrada de texto, filtrar los datos por ciudad (solicitud de filtrado puede enviarse con algún botón de envío oa través de javascript)

Gracias yoy por todas las publicaciones.

Respuesta

2

Si bien esta no es su pregunta, esto suena como una solución perfecta para Django-Selectables puede con solo unas líneas agregar un Formulario de CharField con AJAX que tendrá sus entradas seleccionadas de la lista de ciudades. Eche un vistazo a las muestras enumeradas en el enlace de arriba.

+0

Esto no es realmente lo que estaba buscando. Mi problema es mostrar el filtro de entrada de texto de trabajo. La característica de autocompletar es agradable y me gustaría agregarla más tarde. De todos modos, gracias por tu respuesta. – Jazzuell

+0

ok, pensé esto por mi cuenta. Creé mi propio filtro en filterspecs.py (sé que es una manera desagradable de hacerlo). Si lo intenta de esta manera, tenga cuidado al registrar su filtro. Su filtro debe registrarse antes de los filtros del sistema. Que en models.py asigne su filtro para atribuirle que pertenece. En el filtro utilicé algo que cambia la URL publicada donde están los parámetros. El filtrado por una ciudad se realiza por ciudad = Praga, pero si desea filtrar por lista de filtros, use city__in = Prague, Wien, Dublin. Hay muchas formas más agradables de hacerlo (consultas, AJAX, ...) pero solo estoy aprendiendo. – Jazzuell

12

En caso de que alguien todavía necesite esto. Es poco hackish en plantilla, pero implementado sin una pieza de js.

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'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 
     },) 

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

Entonces, de acuerdo a sus modelos

Listo para el uso del filtro se vería así.

+1

¡Muchas gracias por este fragmento de código! Me acabas de ahorrar algunas horas de trabajo. Sin embargo, hay un pequeño error en su ejemplo: la llamada 'CatalogCityFilter.queryset' debe devolver el queryset. – devsnd

+0

@devsnd ¿Este código plantea una excepción en su caso? Como veo de fuentes django: para FILTER_SPEC en self.filter_specs: new_qs = filter_spec.queryset (request, QS) si new_qs no es Ninguno: qs = new_qs https://github.com/django/django/ blob/master/django/contrib/admin/views/main.py # L325 si el filtro retorna No pasa nada. –

+0

Sin excepción, pero no funcionó antes de devolver el queryset, ya que el 'filter' devuelve una nueva copia del queryset con el filtro aplicado. – devsnd

0

estoy corriendo Django 1.10, 1.11 y r_black 's solution no lo hizo completamente en forma, porque Django se quejaba de que los campos de filtro deben heredar de 'FieldListFilter'.

Así que un cambio simple para que el filtro herede de FieldListFilter se ocupó de quejarse de Django y no tener que especificar una nueva clase para cada campo, ambas al mismo tiempo.

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 (sin cambios):

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

Uso:

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