27Oct

Pasos para la creación de un filtro personalizado en Django implicando múltiples tablas

Guía para crear un filtro personalizado en los listados del Administrador de Django

Nuestro modelo

Teniendo un modelo de **Clientes** que tienen asignada una categoría de segundo nivel asignada a una categoría de primer nivel con la siguiente estructura y modelo.

# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=255, null=False, unique=True, verbose_name=u'Nombre')
    slug = models.CharField(max_length=255, verbose_name=u'Slug', db_index=True)
    is_main = models.BooleanField(default=False, null=False, verbose_name=u'Categoría principal')
    published = models.BooleanField(default=False, null=False, verbose_name=u'Publicada')

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'Categoría'
        verbose_name_plural = u'Categorías'

class SubCategory(models.Model):
    name = models.CharField(max_length=255, null=False, verbose_name=u'Nombre')
    slug = models.CharField(populate_from='name', verbose_name=u'Slug')
    category = models.ForeignKey(Category, unique=False, null=False, verbose_name=u'Categoría')
    published = models.BooleanField(default=False, null=False, verbose_name=u'Publicada')

    def __unicode__(self):
        return "{name} ({category})".format(name=self.name, category=self.category.name)

    class Meta:
        verbose_name = u'Subcategoría'
        verbose_name_plural = u'Subcategorías'

class Customer(models.Model):
    sub_category = models.ForeignKey(SubCategory, unique=False, null=False, verbose_name=u'Subcategoría')
    name = models.CharField(max_length=100, null=False, unique=False, verbose_name=u'Nombre',
                            help_text=u'Nombre del proveedor')
    slug = models.CharField(max_length=100, verbose_name=u'Slug', db_index=True, unique=True)

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'Cliente'
        verbose_name_plural = u'Clientes'

    @property
    def category_name(self):
        return str(self.sub_category.category)

Un cliente tiene asignada una categoría de segundo nivel (Subcategoría), y una categoría de segundo nivel tiene asignada una categoría de primer nivel (Categoría) que puede ser principal o no por el campo is_main.

Queremos que en el listado de Django podamos filtrar por las categorías de primer nivel que sean principales.

Crear el filtro

Dentro de la aplicación de Django donde esté el modelo de datos, creamos una carpeta filter y añadimos el archivo del nuevo filtro customer_category.py:

customer/
    filter/
        customer_category.py
    migrations/
    test/
    __init__.py
    admin.py
    apps.py
    models.py
    tests.py
    urls.py
    views.py

customer_category.py

# -*- coding: utf-8 -*-

from django.contrib import admin
from customer.models import Customer
from customer.models import Category

class CustomerCategoryFilter(admin.SimpleListFilter):
    
    def lookups(self, request, model_admin):
        # Seleccionamos las categorías que son principales ordenadas por nombre ascendente
        all_categories = Category.objects.filter(is_main=True).order_by("name").all()

        response = list()

        for category_entity in all_categories:
            response.append((category_entity.id, category_entity.name))

        return response

    title = u'Categoría Principal'

    parameter_name = 'main_category'

    def queryset(self, request, queryset):

        if self.value():
            category_id = self.value()
            return queryset.filter(customer__sub_category__category__id=int(category_id))

        return queryset

Añadir el filtro al administrador

Una vez creado las clase del filtro, ahora tenemos que añadir ese filtro a nuestro administrador de clientes en el archivo admin.py

# -*- coding: utf-8 -*-

from django.contrib import admin
from . filter.customer_category import CustomerCategoryFilter

# ...
class CustomerAdmin(admin.ModelAdmin):
    list_display = ('name', 'slug', 'sub_category',)
    list_filter = (CustomerCategoryFilter,)
# ...

Ahora en nuestro administrador de Django, en el listado de clientes tendremos listadas las categorías principales y podremos filtrar los resultados por cada una de ellas.

Proyecto Django: https://www.djangoproject.com/

Leave a comment