Commit e293d82c authored by Julien Phalip's avatar Julien Phalip
Browse files

[1.3.X] Fixed #17972 -- Ensured that admin filters on a foreign key respect...

[1.3.X] Fixed #17972 -- Ensured that admin filters on a foreign key respect 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.3.X@17857 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 0bbe7379
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -74,9 +74,12 @@ class RelatedFilterSpec(FilterSpec):
            self.lookup_title = other_model._meta.verbose_name
        else:
            self.lookup_title = f.verbose_name # use field name
        if hasattr(f, 'rel'):
            rel_name = f.rel.get_related_field().name
        else:
            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_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)
+5 −5
Original line number Diff line number Diff line
@@ -225,18 +225,18 @@ class BaseModelAdmin(object):
        # if foo has been specificially included in the lookup list; so
        # drop __id if it is the last part. However, first we need to find
        # the pk attribute name.
        pk_attr_name = None
        rel_name = None
        for part in parts[:-1]:
            field, _, _, _ = model._meta.get_field_by_name(part)
            if hasattr(field, 'rel'):
                model = field.rel.to
                pk_attr_name = model._meta.pk.name
                rel_name = field.rel.get_related_field().name
            elif isinstance(field, RelatedObject):
                model = field.model
                pk_attr_name = model._meta.pk.name
                rel_name = model._meta.pk.name
            else:
                pk_attr_name = None
        if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name:
                rel_name = None
        if rel_name and len(parts) > 1 and parts[-1] == rel_name:
            parts.pop()

        try:
+15 −0
Original line number Diff line number Diff line
@@ -21,3 +21,18 @@ class BoolTest(models.Model):
        default=NO,
        choices=YES_NO_CHOICES
    )


class Department(models.Model):
    code = models.CharField(max_length=4, unique=True)
    description = models.CharField(max_length=50, blank=True, null=True)

    def __unicode__(self):
        return self.description

class Employee(models.Model):
    department = models.ForeignKey(Department, to_field="code")
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name
+66 −2
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from django.utils.encoding import force_unicode

from models import Book, BoolTest
from models import Book, BoolTest, Employee, Department

def select_by(dictlist, key, value):
    return [x for x in dictlist if x[key] == value][0]
@@ -32,7 +32,6 @@ class FilterSpecsTests(TestCase):

        self.request_factory = RequestFactory()


    def get_changelist(self, request, model, modeladmin):
        return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links,
            modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields,
@@ -200,6 +199,67 @@ class FilterSpecsTests(TestCase):
        self.assertEqual(choice['selected'], True)
        self.assertEqual(choice['query_string'], '?completed__exact=1')

    def test_fk_with_to_field(self):
        """
        Ensure that a filter on a FK respects the FK's to_field attribute.
        Refs #17972.
        """
        modeladmin = EmployeeAdmin(Employee, admin.site)

        dev = Department.objects.create(code='DEV', description='Development')
        design = Department.objects.create(code='DSN', description='Design')
        john = Employee.objects.create(name='John Blue', department=dev)
        jack = Employee.objects.create(name='Jack Red', department=design)

        request = self.request_factory.get('/', {})
        changelist = self.get_changelist(request, Employee, modeladmin)

        # Make sure the correct queryset is returned
        queryset = changelist.get_query_set()
        self.assertEqual(list(queryset), [jack, john])

        filterspec = changelist.get_filters(request)[0][-1]
        self.assertEqual(force_unicode(filterspec.title()), u'department')
        choices = list(filterspec.choices(changelist))

        self.assertEqual(choices[0]['display'], u'All')
        self.assertEqual(choices[0]['selected'], True)
        self.assertEqual(choices[0]['query_string'], '?')

        self.assertEqual(choices[1]['display'], u'Development')
        self.assertEqual(choices[1]['selected'], False)
        self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')

        self.assertEqual(choices[2]['display'], u'Design')
        self.assertEqual(choices[2]['selected'], False)
        self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN')

        # Filter by Department=='Development' --------------------------------

        request = self.request_factory.get('/', {'department__code__exact': 'DEV'})
        changelist = self.get_changelist(request, Employee, modeladmin)

        # Make sure the correct queryset is returned
        queryset = changelist.get_query_set()
        self.assertEqual(list(queryset), [john])

        filterspec = changelist.get_filters(request)[0][-1]
        self.assertEqual(force_unicode(filterspec.title()), u'department')
        choices = list(filterspec.choices(changelist))

        self.assertEqual(choices[0]['display'], u'All')
        self.assertEqual(choices[0]['selected'], False)
        self.assertEqual(choices[0]['query_string'], '?')

        self.assertEqual(choices[1]['display'], u'Development')
        self.assertEqual(choices[1]['selected'], True)
        self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')

        self.assertEqual(choices[2]['display'], u'Design')
        self.assertEqual(choices[2]['selected'], False)
        self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN')


class CustomUserAdmin(UserAdmin):
    list_filter = ('books_authored', 'books_contributed')

@@ -209,3 +269,7 @@ class BookAdmin(admin.ModelAdmin):

class BoolTestAdmin(admin.ModelAdmin):
    list_filter = ('completed',)

class EmployeeAdmin(admin.ModelAdmin):
    list_display = ['name', 'department']
    list_filter = ['department']
 No newline at end of file