diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
index ed6c41f52eb4..746537e9c5f8 100644
--- a/django/contrib/admin/utils.py
+++ b/django/contrib/admin/utils.py
@@ -435,6 +435,7 @@ def help_text_for_field(name, model):
def display_for_field(value, field, empty_value_display, avoid_link=False):
from django.contrib.admin.templatetags.admin_list import _boolean_icon
+ from django.db.models.expressions import DatabaseDefault
if field.name == "password" and field.model == get_user_model():
return render_password_as_hash(value)
@@ -450,8 +451,10 @@ def display_for_field(value, field, empty_value_display, avoid_link=False):
# BooleanField needs special-case null-handling, so it comes before the
# general null test.
elif isinstance(field, models.BooleanField):
+ if isinstance(value, DatabaseDefault):
+ return _boolean_icon(None)
return _boolean_icon(value)
- elif value in field.empty_values:
+ elif value in field.empty_values or isinstance(value, DatabaseDefault):
return empty_value_display
elif isinstance(field, models.DateTimeField):
return formats.localize(timezone.template_localtime(value))
@@ -476,12 +479,15 @@ def display_for_field(value, field, empty_value_display, avoid_link=False):
def display_for_value(value, empty_value_display, boolean=False):
from django.contrib.admin.templatetags.admin_list import _boolean_icon
+ from django.db.models.expressions import DatabaseDefault
if boolean:
+ if value in EMPTY_VALUES or isinstance(value, DatabaseDefault):
+ return _boolean_icon(None)
return _boolean_icon(value)
if isinstance(value, str) and not isinstance(value, SafeString):
value = value.strip()
- if value in EMPTY_VALUES:
+ if value in EMPTY_VALUES or isinstance(value, DatabaseDefault):
return empty_value_display
elif isinstance(value, bool):
return str(value)
diff --git a/django/core/serializers/jsonl.py b/django/core/serializers/jsonl.py
index 7bc9bed79f54..c9aa0d0879bd 100644
--- a/django/core/serializers/jsonl.py
+++ b/django/core/serializers/jsonl.py
@@ -47,7 +47,7 @@ def __init__(self, stream_or_string, **options):
stream_or_string = stream_or_string.decode()
if isinstance(stream_or_string, str):
stream_or_string = stream_or_string.splitlines()
- super().__init__(Deserializer._get_lines(stream_or_string), **options)
+ super().__init__(self._get_lines(stream_or_string), **options)
def _handle_object(self, obj):
try:
diff --git a/tests/admin_utils/tests.py b/tests/admin_utils/tests.py
index eced9d206e99..15b09ca812ac 100644
--- a/tests/admin_utils/tests.py
+++ b/tests/admin_utils/tests.py
@@ -21,6 +21,7 @@
from django.contrib.auth.templatetags.auth import render_password_as_hash
from django.core.validators import EMPTY_VALUES
from django.db import DEFAULT_DB_ALIAS, models
+from django.db.models.expressions import DatabaseDefault
from django.test import SimpleTestCase, TestCase, override_settings, skipUnlessDBFeature
from django.test.utils import isolate_apps
from django.utils.formats import localize
@@ -194,6 +195,12 @@ def test_empty_value_display_for_field(self):
)
self.assertEqual(display_value, self.empty_value)
+ def test_empty_value_database_default_display_for_field(self):
+ display_value = display_for_field(
+ DatabaseDefault(models.Value(1)), models.IntegerField(), self.empty_value
+ )
+ self.assertEqual(display_value, self.empty_value)
+
def test_empty_value_display_choices(self):
model_field = models.CharField(choices=((None, "test_none"),))
display_value = display_for_field(None, model_field, self.empty_value)
@@ -201,11 +208,13 @@ def test_empty_value_display_choices(self):
def test_empty_value_display_booleanfield(self):
model_field = models.BooleanField(null=True)
- display_value = display_for_field(None, model_field, self.empty_value)
expected = (
f'
'
)
- self.assertHTMLEqual(display_value, expected)
+ for value in [None, DatabaseDefault(models.Value(True))]:
+ with self.subTest(empty_value=value):
+ display_value = display_for_field(value, model_field, self.empty_value)
+ self.assertHTMLEqual(display_value, expected)
def test_json_display_for_field(self):
tests = [
@@ -307,12 +316,26 @@ def test_list_display_for_value_boolean(self):
self.assertEqual(display_for_value(True, ""), "True")
self.assertEqual(display_for_value(False, ""), "False")
+ def test_list_display_for_value_boolean_database_default(self):
+ # DatabaseDefault expression is interpreted as unknown.
+ self.assertEqual(
+ display_for_value(DatabaseDefault(models.Value(True)), "", boolean=True),
+ '
',
+ )
+ self.assertEqual(display_for_value(DatabaseDefault(models.Value(True)), ""), "")
+
def test_list_display_for_value_empty(self):
for value in EMPTY_VALUES:
with self.subTest(empty_value=value):
display_value = display_for_value(value, self.empty_value)
self.assertEqual(display_value, self.empty_value)
+ def test_list_display_for_database_default(self):
+ display_value = display_for_value(
+ DatabaseDefault(models.Value("1")), self.empty_value
+ )
+ self.assertEqual(display_value, self.empty_value)
+
def test_list_display_for_value_consecutive_whitespace(self):
cases = [
(" ", "-empty-"),
diff --git a/tests/serializers/test_jsonl.py b/tests/serializers/test_jsonl.py
index 3137b037a982..49b52533612f 100644
--- a/tests/serializers/test_jsonl.py
+++ b/tests/serializers/test_jsonl.py
@@ -4,6 +4,7 @@
from django.core import serializers
from django.core.serializers.base import DeserializationError
+from django.core.serializers.jsonl import Deserializer as JsonlDeserializer
from django.db import models
from django.test import TestCase, TransactionTestCase
from django.test.utils import isolate_apps
@@ -269,3 +270,22 @@ class JsonSerializerTransactionTestCase(
}""",
]
fwd_ref_str = "\n".join([s.replace("\n", "") for s in fwd_ref_str])
+
+
+class JsonlDeserializerTests(TestCase):
+ def test_subclass_can_override_get_lines(self):
+ collected = []
+
+ class CustomDeserializer(JsonlDeserializer):
+ @staticmethod
+ def _get_lines(stream):
+ for obj in JsonlDeserializer._get_lines(stream):
+ collected.append(obj)
+ yield obj
+
+ data = (
+ '{"pk": 1, "model": "serializers.author", "fields": {"name": "Jane"}}\n'
+ '{"pk": 2, "model": "serializers.author", "fields": {"name": "Joe"}}\n'
+ )
+ list(CustomDeserializer(data))
+ self.assertEqual(len(collected), 2)