Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions django/contrib/admin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion django/core/serializers/jsonl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
27 changes: 25 additions & 2 deletions tests/admin_utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -194,18 +195,26 @@ 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)
self.assertEqual(display_value, "test_none")

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'<img src="{settings.STATIC_URL}admin/img/icon-unknown.svg" alt="None" />'
)
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 = [
Expand Down Expand Up @@ -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),
'<img src="/static/admin/img/icon-unknown.svg" alt="None">',
)
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-"),
Expand Down
20 changes: 20 additions & 0 deletions tests/serializers/test_jsonl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Loading