from django.db import models from django.contrib.admin.filterspecs import FilterSpec, RelatedFilterSpec from django.contrib.admin.util import get_model_from_relation from django.db.models import Count from taggit.managers import TaggableManager class TaggitFilterSpec(RelatedFilterSpec): """ A FilterSpec that can be used to filter by taggit tags in the admin. To use, simply import this module (for example in `models.py`), and add the name of your :class:`taggit.managers.TaggableManager` field in the :attr:`list_filter` attribute of your :class:`django.contrib.ModelAdmin` class. """ def __init__(self, f, request, params, model, model_admin, field_path=None): super(RelatedFilterSpec, self).__init__( f, request, params, model, model_admin, field_path=field_path) other_model = get_model_from_relation(f) if isinstance(f, (models.ManyToManyField, models.related.RelatedObject)): # no direct field on this model, get name from other model self.lookup_title = other_model._meta.verbose_name else: self.lookup_title = f.verbose_name # use field name rel_name = other_model._meta.pk.name self.lookup_kwarg = '%s__%s__exact' % (self.field_path, rel_name) self.lookup_kwarg_isnull = '%s__isnull' % (self.field_path) self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.lookup_val_isnull = request.GET.get( self.lookup_kwarg_isnull, None) # Get tags and their count through_opts = f.through._meta count_field = ("%s_%s_items" % (through_opts.app_label, through_opts.object_name)).lower() queryset = getattr(f.model, f.name).all() queryset = queryset.annotate(num_times=Count(count_field)) queryset = queryset.order_by("-num_times") self.lookup_choices = [(t.pk, "%s (%s)" % (t.name, t.num_times)) for t in queryset] # HACK: we insert the filter at the beginning of the list to avoid the manager # to be associated with a RelatedFilterSpec FilterSpec.filter_specs.insert(0, (lambda f: isinstance(f, TaggableManager), TaggitFilterSpec))