瀏覽代碼

Fixed #33927 -- Fixed crash when displaying ArrayField with choices in admin.

David Wobrock 2 年之前
父節點
當前提交
897f38fabe
共有 2 個文件被更改,包括 46 次插入1 次删除
  1. 9 1
      django/contrib/admin/utils.py
  2. 37 0
      tests/postgres_tests/test_array.py

+ 9 - 1
django/contrib/admin/utils.py

@@ -10,6 +10,7 @@ from django.db.models.deletion import Collector
 from django.forms.utils import pretty_name
 from django.urls import NoReverseMatch, reverse
 from django.utils import formats, timezone
+from django.utils.hashable import make_hashable
 from django.utils.html import format_html
 from django.utils.regex_helper import _lazy_re_compile
 from django.utils.text import capfirst
@@ -401,7 +402,14 @@ def display_for_field(value, field, empty_value_display):
     from django.contrib.admin.templatetags.admin_list import _boolean_icon
 
     if getattr(field, "flatchoices", None):
-        return dict(field.flatchoices).get(value, empty_value_display)
+        try:
+            return dict(field.flatchoices).get(value, empty_value_display)
+        except TypeError:
+            # Allow list-like choices.
+            flatchoices = make_hashable(field.flatchoices)
+            value = make_hashable(value)
+            return dict(flatchoices).get(value, empty_value_display)
+
     # BooleanField needs special-case null-handling, so it comes before the
     # general null test.
     elif isinstance(field, models.BooleanField):

+ 37 - 0
tests/postgres_tests/test_array.py

@@ -5,6 +5,7 @@ import unittest
 import uuid
 
 from django import forms
+from django.contrib.admin.utils import display_for_field
 from django.core import checks, exceptions, serializers, validators
 from django.core.exceptions import FieldError
 from django.core.management import call_command
@@ -1366,3 +1367,39 @@ class TestSplitFormWidget(PostgreSQLWidgetTestCase):
             ),
             False,
         )
+
+
+class TestAdminUtils(PostgreSQLTestCase):
+    empty_value = "-empty-"
+
+    def test_array_display_for_field(self):
+        array_field = ArrayField(models.IntegerField())
+        display_value = display_for_field(
+            [1, 2],
+            array_field,
+            self.empty_value,
+        )
+        self.assertEqual(display_value, "1, 2")
+
+    def test_array_with_choices_display_for_field(self):
+        array_field = ArrayField(
+            models.IntegerField(),
+            choices=[
+                ([1, 2, 3], "1st choice"),
+                ([1, 2], "2nd choice"),
+            ],
+        )
+
+        display_value = display_for_field(
+            [1, 2],
+            array_field,
+            self.empty_value,
+        )
+        self.assertEqual(display_value, "2nd choice")
+
+        display_value = display_for_field(
+            [99, 99],
+            array_field,
+            self.empty_value,
+        )
+        self.assertEqual(display_value, self.empty_value)