From fd3dccb8dd4a7ae0ce5e5e5af25452bfca4462a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20=C5=9Awiat?= Date: Thu, 9 Oct 2014 00:47:48 +0200 Subject: [PATCH] [1.7.x] Fixed #23616 - Fixed generic relations in ModelAdmin.list_filter. Thanks ranjur for reporting bug, timgraham for review, and collinanderson for contributing tips. Backport of 06b11b617e from master --- django/contrib/admin/options.py | 5 ++++- docs/releases/1.7.1.txt | 2 ++ tests/admin_filters/models.py | 22 ++++++++++++++++++++++ tests/admin_filters/tests.py | 24 +++++++++++++++++++++++- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index eac57faf44..cef9021326 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -413,7 +413,10 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)): # since it's ignored in ChangeList.get_filters(). return True model = field.rel.to - rel_name = field.rel.get_related_field().name + if hasattr(field.rel, 'get_related_field'): + rel_name = field.rel.get_related_field().name + else: + rel_name = None elif isinstance(field, RelatedObject): model = field.model rel_name = model._meta.pk.name diff --git a/docs/releases/1.7.1.txt b/docs/releases/1.7.1.txt index 70535b8161..dba3ec9f19 100644 --- a/docs/releases/1.7.1.txt +++ b/docs/releases/1.7.1.txt @@ -112,3 +112,5 @@ Bugfixes * Added a prompt to the migrations questioner when removing the null constraint from a field to prevent an IntegrityError on existing NULL rows (:ticket:`23609`). + +* Fixed generic relations in ``ModelAdmin.list_filter`` (:ticket:`23616`). diff --git a/tests/admin_filters/models.py b/tests/admin_filters/models.py index 4634ebb535..b2debaaf84 100644 --- a/tests/admin_filters/models.py +++ b/tests/admin_filters/models.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals from django.contrib.auth.models import User +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation +from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible @@ -35,3 +37,23 @@ class Employee(models.Model): def __str__(self): return self.name + + +@python_2_unicode_compatible +class TaggedItem(models.Model): + tag = models.SlugField() + content_type = models.ForeignKey(ContentType, related_name='tagged_items') + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + + def __str__(self): + return self.tag + + +@python_2_unicode_compatible +class Bookmark(models.Model): + url = models.URLField() + tags = GenericRelation(TaggedItem) + + def __str__(self): + return self.url diff --git a/tests/admin_filters/tests.py b/tests/admin_filters/tests.py index 17c67929dc..4d7d775910 100644 --- a/tests/admin_filters/tests.py +++ b/tests/admin_filters/tests.py @@ -12,7 +12,7 @@ from django.test import TestCase, RequestFactory, override_settings from django.utils.encoding import force_text from django.utils import six -from .models import Book, Department, Employee +from .models import Book, Department, Employee, Bookmark, TaggedItem def select_by(dictlist, key, value): @@ -184,6 +184,10 @@ class DepartmentFilterDynamicValueBookAdmin(EmployeeAdmin): list_filter = [DepartmentListFilterLookupWithDynamicValue, ] +class BookmarkAdminGenericRelation(ModelAdmin): + list_filter = ['tags__tag'] + + class ListFiltersTests(TestCase): def setUp(self): @@ -464,6 +468,24 @@ class ListFiltersTests(TestCase): self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?books_contributed__id__exact=%d' % self.django_book.pk) + def test_listfilter_genericrelation(self): + django_bookmark = Bookmark.objects.create(url='https://www.djangoproject.com/') + python_bookmark = Bookmark.objects.create(url='https://www.python.org/') + kernel_bookmark = Bookmark.objects.create(url='https://www.kernel.org/') + + TaggedItem.objects.create(content_object=django_bookmark, tag='python') + TaggedItem.objects.create(content_object=python_bookmark, tag='python') + TaggedItem.objects.create(content_object=kernel_bookmark, tag='linux') + + modeladmin = BookmarkAdminGenericRelation(Bookmark, site) + + request = self.request_factory.get('/', {'tags__tag': 'python'}) + changelist = self.get_changelist(request, Bookmark, modeladmin) + queryset = changelist.get_queryset(request) + + expected = [python_bookmark, django_bookmark] + self.assertEqual(list(queryset), expected) + def test_booleanfieldlistfilter(self): modeladmin = BookAdmin(Book, site) self.verify_booleanfieldlistfilter(modeladmin)