El Blog de Trespams

Blog personal sobre tecnologia, gestió de projectes i coses que se me passen pel cap

Filtres a l'admin de Django 1.4

En una de les darreres aplicacions que hem fet, la d'apibaleares hem tirat força de l'administrador de Django. Segurament i donades les característiques que volem que tengui l'apliació al final, anirem cap a un administrador ad-hoc, però ara per començar va força bé.

Un dels primers problemes va sorgir amb els filtres. Amb l'admin de Django és molt senzill fer un filtre per qualsevol camp, però té l'emperò que si el camp és una clau forana cap a una altra taula amb molts elements Django et pinta tots els elements, amb la qual cosa la plana creix molt i el llista es fa mal de manejar.

Amb Django 1.4 tenim l'opció de crear-nos els nostres propis filtres i dir-li a l'admin que els faci servir. Per posar-vos en situació el que voldríem és que enlloc del filtre com a llista per la clau forana, poguéssim seleccionar l'element a filtrar des d'un desplegable. És veritat que perdem la selecció múltiple, però a l'usuari no li fa res, el que no vol és tota la llista al costat dret del llistat.

Així doncs, el primer que farem serà veure què hi ha respecte als filtres. La documentació de Django es minsa, potser perquè ja avisen que això pot canviar en un futur proper. Ens arriscarem per ara!

El filtre que ens interessa el derivarem de RelatedFieldListFilter que és qui se n'encarrega de pintar les claus foranes. Val a dir que no necessitam massa cosa, volem pintar d'una manera diferent els elements, així que únicament hem de sobreescriure la part que se n'encarrega de la visualització:

Dins l'admin.py del nostre projecte, per exemple, fem

from django.contrib.admin import RelatedFieldListFilter class SelectFilter(RelatedFieldListFilter): template ='filter/select.html'

i ara li hem de dir a Django que el faci servir, per això basta fer que el list_filter agafi aquest classe. Per exemple, suposem que la clau forana es diu localitat i que filtram per altres elements com tipus, actiu. Inicialment tindríem:

list_filter = ('actiu', 'tipus', 'localitat')

llavors el que farem serà:

list_filter = ('actiu, 'tipus', ('localitat', SelectFilter) )

és a dir, ara podem dir a Django a més del nom del cap a filtrar, la classe que farem servir per presentar el filtrat.

La nostra classe és molt simple, així que la única cosa que hem de fer és pintar la plantilla.

La plantilla base la podeu trobar al codi font de Django mateix, amb el nom de select.html. La modificarem per a que pinti un desplegable:

{% load i18n %}

<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>

<select id="s_localidad" style="width:150px;margin-left:5px;">

{% for choice in choices %}
<option
{% if choice.selected %}
selected='selected' class='selected'
{% endif %} value="{{ choice.query_string|iriencode }}">
{{ choice.display }}
</option> {% endfor %}
</select>

Nota: el codi ara per ara no és genèric, és la primera aproximació que vaig fer per sortir del pas :)

La idea però és veure com fa Django els filtres. De fet el que està fent és mantenir tota la consulta als paràmetres, així que el que es feia amb la llista ho podem aprofitar també, mostrant el contingut i al value mantenir el link del filtrat.

Això però no acaba de funcionar, ja que ara no tenim un link i necessitam dir-li a Django que apliqui el nou filtre. Com que l'admin de Django duu jQuery per la part de javascript, doncs el podem aprofitar i afegir codi per a que quan canvii l'element cridi a la url amb el nou filtrat:

<script type="text/javascript"> (function($) { $(document).ready(function($) { $("#s_localidad").change(function(){ var ref = $(this, 'option:selected').val(); window.location.href = ref; }); }) })(django.jQuery); </script>

i ja ho tenim! Ara sols queda fer una cosa més genèrica i reaprofitable, però a mi al manco ja m'ha servit per veure que això dels filtres té molt bona pinta.

blog comments powered by Disqus