Search_form¶
Scopo¶
Rimpiazzare la ricerca semplice dell’admin di Django con una ricerca selettiva per campi.
Scenario¶
una patch di sicurezza del 23/12/2010 ha aggiunto un metodo di ModelAdmin
lookup_allowed() che prende un lookup (eg.: user__username) e
ritorna un booleano per sapere se può essere utilizzato o meno.
Questa patch influenza sicuramente django 1.4+ che aggiunge molte modifiche al sistema di filtri di admin.
Qui c’è un blog sul modo di bypassarlo e tanti riferimenti al problema. Djangosnippets, riporta uno snippet che affronta la questione in modo analogo.
In sintesi i filtri ammessi per default sono solo quelli inseriti nella lista
list_filters, in altenativa si può modificare lookup_allowed().
Componenti¶
Un oggetto ChangeList e la sua derivata
jmb.core.admin.main.JmbChangeListQuesta classe ha 2 metodi rilevanti per noi:
- get_filters
che prepara tutti i filtri. Noi prima di questa chiamata modifichiamo la copia dei parametri in GET in modo che siano già stati puliti dai metodi clean_* della form
- get_query_set
ritorna il queryset applicando i filtri
La form necessaria per creare il link che verrà usato in GET. Questa form viene creata automaticamente a partire dal campo
ModelAdmin.advanced_search_fieldsUn inclusion templatetag (
advanced_search_form) che renderizza la form e che a sua volta utilizza il templateadvanced_search_form.htmle viene chiamato così:{% block advanced_search %}{% advanced_search_form cl %}{% endblock %}
Il tag riceve quindi l’oggetto
ChangeListin argomento ed utilizza il suo metodo.cl.model_admin.get_search_formper trovare lasearch_formidonea.:attr:
jmb.core.admin.options.ExtendedModelAdmin.lookup_allowed
Utilizzo¶
Nella implementazione più semplice basta creare l’attributo
ModelAdmin.advanced_search_fields come tupla di tuple con field_names:
class CertificateAdmin(ExtendedModelAdmin):
model = Certificates
advanced_search_fields = (
('start_date__gte','user'),
('user__username__icontains', 'user__last_name__icontains', ),
('status', 'description__icontains',),
)
Nel caso serva una personalizzazione più ricca è possibile utilizzare il
metodo ModelAdmin.get_filterset e fare ritornare una classe che eredita
da :class:django_filters.Filterset.
django-filters¶
Rispetto al pacchetto originario di django-filters le nostre aggiunte
permettono di:
dichiarare il lookup_type nel nome del campo. È quindi possibile srivere
start__date__gteper impostare come lookup_typegte. In questo modo non è necessario dichiarare il filter in modo dichiarativo solo per l’impostazione dellookup_typeriempire i campi con choices secondo il field dichiarato nel modello
aggiunge ai booleani l’opzione nulla (
-----)quando il
lookup_typeèinviene usato il widget di scelta multiplaquando una data ha il
lookup_typerangeviene usato un widget che permette di scegliere fra oggi, ieri, ultimi 7 giorni, ultimo mese, mese precedente, anno in corso, anno precedente.
Altri filtri¶
Nella change list compaiono i bottoni per ricerca semplice, ricerca avanzata e filtri secondo questa logica:
- ricerca semplice
se definito l’attributo
search_fields- ricerca avanzata
se definito
advanced_search_fieldsin modeladmin o l’attributofilterset_class- filtri
se definito l’attributo list_filter
Personalizzazioni¶
Jumbo (per Django 1.2.7) utilizzava una JChangeList preparata per manipolare
le date utilizzando django.utils.translation.get_format_date`() che
ora è soppiantato da django.utils.format_date.
Con l’attuale sistema le date vengono interpretate come localizzate.
Implementazione¶
L’attuale implementazione di basa su questi elementi:
- django-filters
modificata da noi, vedi sopra
- change_list.html
- il template usato è quello contenuto in
jmb.core È importante che
admin/change_list.htmldijmb.corevenga usato al posto di quello didjango.contrib.admin, questo è ottenuto impostando nelleinstalled_appsjmb.coreal primo posto (il template.loader.app_directories usa INSTALLED_APPS ): qui vengono messi 2 nuovi templatetags
- advanced_search_form
un templatetag che implementa la ricerca avanzata
- jsearch_form
un template tag che implementa la toolbar della ricerca
- il template usato è quello contenuto in
- ExtendibleModelAdmin.setup_advanced_search
crea il filterset a partire da
advanced_search_fieldsosearch_filterset_classoget_filterset__class- ExtendibleModelAdmin.lookup_allowed
permette ogni
search_field- ExtendibleModelAdminget.changelist
return JmbChangeList
- ExtendibleModelAdminget.queryset
Se c’è un filtro, qui viene restituito il qs generato da search_filterset.qs
- JmbChangeList.get_filters
All’interno di questo metodo pulisco self.params
- advanced_search.js
gestione di visibilità advanced_search_form
serialize dei soli campi non vuoti