Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.4.X] Fixed #17972 -- Ensured that admin filters on a foreign key r…

…espect the to_field attribute. This fixes a regression introduced in [14674] and Django 1.3. Thanks to graveyboat and Karen Tracey for the report.

Backport of r17854 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17858 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit aafa73db546683edbaefdd17859f2722349e9373 1 parent 1382297
Julien Phalip authored March 31, 2012
5  django/contrib/admin/filters.py
@@ -155,7 +155,10 @@ def create(cls, field, request, params, model, model_admin, field_path):
155 155
 class RelatedFieldListFilter(FieldListFilter):
156 156
     def __init__(self, field, request, params, model, model_admin, field_path):
157 157
         other_model = get_model_from_relation(field)
158  
-        rel_name = other_model._meta.pk.name
  158
+        if hasattr(field, 'rel'):
  159
+            rel_name = field.rel.get_related_field().name
  160
+        else:
  161
+            rel_name = other_model._meta.pk.name
159 162
         self.lookup_kwarg = '%s__%s__exact' % (field_path, rel_name)
160 163
         self.lookup_kwarg_isnull = '%s__isnull' % field_path
161 164
         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
10  django/contrib/admin/options.py
@@ -245,7 +245,7 @@ def lookup_allowed(self, lookup, value):
245 245
         # if foo has been specificially included in the lookup list; so
246 246
         # drop __id if it is the last part. However, first we need to find
247 247
         # the pk attribute name.
248  
-        pk_attr_name = None
  248
+        rel_name = None
249 249
         for part in parts[:-1]:
250 250
             try:
251 251
                 field, _, _, _ = model._meta.get_field_by_name(part)
@@ -255,13 +255,13 @@ def lookup_allowed(self, lookup, value):
255 255
                 return True
256 256
             if hasattr(field, 'rel'):
257 257
                 model = field.rel.to
258  
-                pk_attr_name = model._meta.pk.name
  258
+                rel_name = field.rel.get_related_field().name
259 259
             elif isinstance(field, RelatedObject):
260 260
                 model = field.model
261  
-                pk_attr_name = model._meta.pk.name
  261
+                rel_name = model._meta.pk.name
262 262
             else:
263  
-                pk_attr_name = None
264  
-        if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name:
  263
+                rel_name = None
  264
+        if rel_name and len(parts) > 1 and parts[-1] == rel_name:
265 265
             parts.pop()
266 266
 
267 267
         if len(parts) == 1:
15  tests/regressiontests/admin_filters/models.py
@@ -13,3 +13,18 @@ class Book(models.Model):
13 13
 
14 14
     def __unicode__(self):
15 15
         return self.title
  16
+
  17
+
  18
+class Department(models.Model):
  19
+    code = models.CharField(max_length=4, unique=True)
  20
+    description = models.CharField(max_length=50, blank=True, null=True)
  21
+
  22
+    def __unicode__(self):
  23
+        return self.description
  24
+
  25
+class Employee(models.Model):
  26
+    department = models.ForeignKey(Department, to_field="code")
  27
+    name = models.CharField(max_length=100)
  28
+
  29
+    def __unicode__(self):
  30
+        return self.name
68  tests/regressiontests/admin_filters/tests.py
@@ -4,7 +4,6 @@
4 4
 
5 5
 from django.contrib.admin import (site, ModelAdmin, SimpleListFilter,
6 6
     BooleanFieldListFilter)
7  
-from django.contrib.admin.options import IncorrectLookupParameters
8 7
 from django.contrib.admin.views.main import ChangeList
9 8
 from django.contrib.auth.admin import UserAdmin
10 9
 from django.contrib.auth.models import User
@@ -13,7 +12,7 @@
13 12
 from django.test.utils import override_settings
14 13
 from django.utils.encoding import force_unicode
15 14
 
16  
-from .models import Book
  15
+from .models import Book, Department, Employee
17 16
 
18 17
 
19 18
 def select_by(dictlist, key, value):
@@ -113,6 +112,9 @@ class DecadeFilterBookAdminParameterEndsWith__In(ModelAdmin):
113 112
 class DecadeFilterBookAdminParameterEndsWith__Isnull(ModelAdmin):
114 113
     list_filter = (DecadeListFilterParameterEndsWith__Isnull,)
115 114
 
  115
+class EmployeeAdmin(ModelAdmin):
  116
+    list_display = ['name', 'department']
  117
+    list_filter = ['department']
116 118
 
117 119
 
118 120
 class ListFiltersTests(TestCase):
@@ -633,4 +635,64 @@ def test_parameter_ends_with__in__or__isnull(self):
633 635
         choices = list(filterspec.choices(changelist))
634 636
         self.assertEqual(choices[2]['display'], u'the 1990\'s')
635 637
         self.assertEqual(choices[2]['selected'], True)
636  
-        self.assertEqual(choices[2]['query_string'], '?decade__isnull=the+90s')
  638
+        self.assertEqual(choices[2]['query_string'], '?decade__isnull=the+90s')
  639
+
  640
+    def test_fk_with_to_field(self):
  641
+        """
  642
+        Ensure that a filter on a FK respects the FK's to_field attribute.
  643
+        Refs #17972.
  644
+        """
  645
+        modeladmin = EmployeeAdmin(Employee, site)
  646
+
  647
+        dev = Department.objects.create(code='DEV', description='Development')
  648
+        design = Department.objects.create(code='DSN', description='Design')
  649
+        john = Employee.objects.create(name='John Blue', department=dev)
  650
+        jack = Employee.objects.create(name='Jack Red', department=design)
  651
+
  652
+        request = self.request_factory.get('/', {})
  653
+        changelist = self.get_changelist(request, Employee, modeladmin)
  654
+
  655
+        # Make sure the correct queryset is returned
  656
+        queryset = changelist.get_query_set(request)
  657
+        self.assertEqual(list(queryset), [john, jack])
  658
+
  659
+        filterspec = changelist.get_filters(request)[0][-1]
  660
+        self.assertEqual(force_unicode(filterspec.title), u'department')
  661
+        choices = list(filterspec.choices(changelist))
  662
+
  663
+        self.assertEqual(choices[0]['display'], u'All')
  664
+        self.assertEqual(choices[0]['selected'], True)
  665
+        self.assertEqual(choices[0]['query_string'], '?')
  666
+
  667
+        self.assertEqual(choices[1]['display'], u'Development')
  668
+        self.assertEqual(choices[1]['selected'], False)
  669
+        self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')
  670
+
  671
+        self.assertEqual(choices[2]['display'], u'Design')
  672
+        self.assertEqual(choices[2]['selected'], False)
  673
+        self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN')
  674
+
  675
+        # Filter by Department=='Development' --------------------------------
  676
+
  677
+        request = self.request_factory.get('/', {'department__code__exact': 'DEV'})
  678
+        changelist = self.get_changelist(request, Employee, modeladmin)
  679
+
  680
+        # Make sure the correct queryset is returned
  681
+        queryset = changelist.get_query_set(request)
  682
+        self.assertEqual(list(queryset), [john])
  683
+
  684
+        filterspec = changelist.get_filters(request)[0][-1]
  685
+        self.assertEqual(force_unicode(filterspec.title), u'department')
  686
+        choices = list(filterspec.choices(changelist))
  687
+
  688
+        self.assertEqual(choices[0]['display'], u'All')
  689
+        self.assertEqual(choices[0]['selected'], False)
  690
+        self.assertEqual(choices[0]['query_string'], '?')
  691
+
  692
+        self.assertEqual(choices[1]['display'], u'Development')
  693
+        self.assertEqual(choices[1]['selected'], True)
  694
+        self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')
  695
+
  696
+        self.assertEqual(choices[2]['display'], u'Design')
  697
+        self.assertEqual(choices[2]['selected'], False)
  698
+        self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN')

0 notes on commit aafa73d

Please sign in to comment.
Something went wrong with that request. Please try again.