소스 검색

Fixed #27356 -- Fixed ModelAdmin.lookup_allowed() for some nested relations.

Anton Samarchyan 8 년 전
부모
커밋
b27166b769
2개의 변경된 파일39개의 추가작업 그리고 6개의 파일을 삭제
  1. 10 6
      django/contrib/admin/options.py
  2. 29 0
      tests/modeladmin/tests.py

+ 10 - 6
django/contrib/admin/options.py

@@ -371,16 +371,20 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass):
         if len(relation_parts) <= 1:
             # Either a local field filter, or no fields at all.
             return True
-        clean_lookup = LOOKUP_SEP.join(relation_parts)
-        valid_lookups = [self.date_hierarchy]
+        valid_lookups = {self.date_hierarchy}
         for filter_item in self.list_filter:
             if isinstance(filter_item, type) and issubclass(filter_item, SimpleListFilter):
-                valid_lookups.append(filter_item.parameter_name)
+                valid_lookups.add(filter_item.parameter_name)
             elif isinstance(filter_item, (list, tuple)):
-                valid_lookups.append(filter_item[0])
+                valid_lookups.add(filter_item[0])
             else:
-                valid_lookups.append(filter_item)
-        return clean_lookup in valid_lookups
+                valid_lookups.add(filter_item)
+
+        # Is it a valid relational lookup?
+        return not {
+            LOOKUP_SEP.join(relation_parts),
+            LOOKUP_SEP.join(relation_parts + [part])
+        }.isdisjoint(valid_lookups)
 
     def to_field_allowed(self, request, to_field):
         """

+ 29 - 0
tests/modeladmin/tests.py

@@ -8,8 +8,10 @@ from django.contrib.admin.options import (
 from django.contrib.admin.sites import AdminSite
 from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect
 from django.contrib.auth.models import User
+from django.db import models
 from django.forms.widgets import Select
 from django.test import SimpleTestCase, TestCase
+from django.test.utils import isolate_apps
 
 from .models import Band, Concert
 
@@ -90,6 +92,33 @@ class ModelAdminTests(TestCase):
         ma = BandAdmin(Band, self.site)
         self.assertTrue(ma.lookup_allowed('name__nonexistent', 'test_value'))
 
+    @isolate_apps('modeladmin')
+    def test_lookup_allowed_onetoone(self):
+        class Department(models.Model):
+            code = models.CharField(max_length=4, unique=True)
+
+        class Employee(models.Model):
+            department = models.ForeignKey(Department, models.CASCADE, to_field="code")
+
+        class EmployeeProfile(models.Model):
+            employee = models.OneToOneField(Employee, models.CASCADE)
+
+        class EmployeeInfo(models.Model):
+            employee = models.OneToOneField(Employee, models.CASCADE)
+            description = models.CharField(max_length=100)
+
+        class EmployeeProfileAdmin(ModelAdmin):
+            list_filter = [
+                'employee__employeeinfo__description',
+                'employee__department__code',
+            ]
+
+        ma = EmployeeProfileAdmin(EmployeeProfile, self.site)
+        # Reverse OneToOneField
+        self.assertIs(ma.lookup_allowed('employee__employeeinfo__description', 'test_value'), True)
+        # OneToOneField and ForeignKey
+        self.assertIs(ma.lookup_allowed('employee__department__code', 'test_value'), True)
+
     def test_field_arguments(self):
         # If fields is specified, fieldsets_add and fieldsets_change should
         # just stick the fields into a formsets structure and return it.