Custom filter for filter_horizontal admin in django
I have the following models where a deck have a many to many relationship with problems and problems can have tags
from django.utils import timezone
from django.db import models
from taggit.models import TaggedItemBase
from taggit.managers import TaggableManager
# Create your models here.
class TaggedProblem(TaggedItemBase):
content_object = models.ForeignKey('Problem', on_delete=models.CASCADE)
class Problem(models.Model):
title = models.CharField(max_length=200)
body = models.CharField(max_length=10000)
pub_date = models.DateTimeField("date published", default=timezone.now())
tags = TaggableManager(through=TaggedProblem)
class Meta:
verbose_name = "problem"
verbose_name_plural = "problems"
def __str__(self):
return self.title
class Deck(models.Model):
name = models.CharField(max_length=200)
problems = models.ManyToManyField(Problem)
def __str__(self):
return self.name
then for the admin i have the following
from django.contrib import admin
# Register your models here.
from .models import Problem,Deck
class DeckAdmin(admin.ModelAdmin):
filter_horizontal = ('problems',)
admin.site.register(Deck, DeckAdmin)
admin.site.register(Problem)
well what i want to do is to have a custom filter to filter the available problems, the filter must be an interface where i can include and exclude tags associated with the problems, so i want to replace the filter search box with something like this
so i can filter the problems by tags and then add then to the deck, how can i achieve that functionality?, i am new to django and have no idea how to proceed
I think the key is to create a custom ModelForm that dynamically filters the problems queryset based on tag criteria before the horizontal filter widget is rendered.
Something like this
# admin.py
from django import forms
from django.contrib import admin
from .models import Problem, Deck
class DeckAdminForm(forms.ModelForm):
include_tags = forms.CharField(
required=False,
help_text="Enter tags to include (comma-separated)"
)
exclude_tags = forms.CharField(
required=False,
help_text="Enter tags to exclude (comma-separated)"
)
class Meta:
model = Deck
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Get filter parameters from form data
include_tags = self.data.get('include_tags', '') if self.data else ''
exclude_tags = self.data.get('exclude_tags', '') if self.data else ''
if include_tags or exclude_tags:
# Start with all problems
queryset = Problem.objects.all()
# Apply include filter
if include_tags:
include_list = [tag.strip() for tag in include_tags.split(',') if tag.strip()]
queryset = queryset.filter(tags__name__in=include_list).distinct()
# Apply exclude filter
if exclude_tags:
exclude_list = [tag.strip() for tag in exclude_tags.split(',') if tag.strip()]
queryset = queryset.exclude(tags__name__in=exclude_list).distinct()
# Update the problems field queryset
self.fields['problems'].queryset = queryset
class DeckAdmin(admin.ModelAdmin):
form = DeckAdminForm
filter_horizontal = ('problems',)
admin.site.register(Deck, DeckAdmin)
admin.site.register(Problem)
Also (alternative solution), you can create a custom admin form with a specialized widget that combines tag filtering with the horizontal filter interface but it's a bit complicated (js, css..)

