Login

django admin filter for GenericForeignKey field

Author:
dkoldyaev
Posted:
April 21, 2016
Language:
Python
Version:
1.7
Score:
0 (after 0 ratings)

Simple filter for django ModelAdmin

How use:

#models.py
class ObjectWithGenericForeignKey(model.Model):
    content_type =  models.ForeignKey(ContentType)
    object_id =     models.PositiveIntegerField()
    content_object= GenericForeignKey('content_type', 'object_id', for_concrete_model=False)

#admin.py
class CommentAdmin(admin.ModelAdmin):
    list_filter = (get_generic_foreign_key_filter(u'Filter title'),)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from django.contrib import admin
from django.contrib.contenttypes.models import ContentType

def get_generic_foreign_key_filter(title, parameter_name=u'', separator='-', content_type_id_field='content_type_id', object_id_field='object_id') :

    class GenericForeignKeyFilter(admin.SimpleListFilter):

        def __init__(self, request, params, model, model_admin):
            self.separator = separator
            self.title = title
            self.parameter_name = u'generic_foreign_key_' + parameter_name
            super(GenericForeignKeyFilter, self).__init__(request, params, model, model_admin)

        def lookups(self, request, model_admin):
            qs = model_admin.model.objects.all()\
                .order_by(content_type_id_field, object_id_field)\
                .distinct(content_type_id_field, object_id_field)\
                .values_list(content_type_id_field, object_id_field)
            return [
                (
                    '{1}{0.separator}{2}'.format(self, *content_type_and_obj_id_pair),
                    ContentType.objects
                        .get(id=content_type_and_obj_id_pair[0])
                        .model_class()
                        .objects.get(pk=content_type_and_obj_id_pair[1])
                        .__unicode__()
                )
                for content_type_and_obj_id_pair
                in qs
            ]

        def queryset(self, request, queryset):
            try :
                content_type_id, object_id = self.value().split(self.separator)
                return queryset.filter(**({
                    content_type_id_field:content_type_id,
                    object_id_field:object_id
                }))
            except:
                return queryset

    return GenericForeignKeyFilter

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Guest007 (on December 8, 2016):

Great solution!! I'm just change .__unicode__() to .__str__() and qs = model_admin.model.objects.all()\ to filter = object_id_field + '__isnull' qs = model_admin.model.objects.exclude(**{ filter: True })\ for use with null=True fields

#

RxnDbr (on June 8, 2017):

Using django 1.11 and mysql,

I had to change __unicode__() to __str__()

and qs didn't worked because of distinct (NotImplementedError: DISTINCT ON fields is not supported by this database backend), so I have changed it in this way --> qs = model_admin.model.objects.all().order_by(content_type_id_field, object_id_field) .values_list(content_type_id_field, object_id_field).distinct()

Now it works fine, thank you!

#

frnhr (on December 3, 2024):

Nice! Works with Django 4.2 with minimal fixes (str instead of unicode).

#

Please login first before commenting.