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
3 changes: 1 addition & 2 deletions django/contrib/admin/static/admin/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,6 @@ ol.breadcrumbs li {
display: inline-block;
font-size: 0.875rem;
padding: 0;
line-height: 0;
}

ol.breadcrumbs li:not([aria-current="page"])::after {
Expand Down Expand Up @@ -807,7 +806,7 @@ ol.breadcrumbs a:focus, ol.breadcrumbs a:hover {

.changelink, .inlinechangelink {
padding-left: 16px;
background: url(../img/icon-changelink.svg) 0 1px no-repeat;
background: url(../img/icon-changelink.svg) 0 2px no-repeat;
}

.deletelink {
Expand Down
8 changes: 8 additions & 0 deletions django/contrib/admin/static/admin/css/forms.css
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,14 @@ form .related-widget-wrapper ul {
padding-left: 0;
}

form .related-widget-wrapper a.change-related img {
transform: scale(0.85);
}

form .related-widget-wrapper a.view-related img {
transform: scale(0.95);
}

.clearable-file-input input {
margin-top: 0;
}
4 changes: 4 additions & 0 deletions django/contrib/admin/static/admin/css/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,10 @@ ul.timelist, .timelist li {
margin-bottom: 5px;
}

.related-widget-wrapper:has(select:not([multiple])) {
align-items: center;
}

.related-widget-wrapper-link {
opacity: .6;
filter: grayscale(1);
Expand Down
2 changes: 1 addition & 1 deletion django/contrib/admin/static/admin/img/icon-deletelink.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 10 additions & 2 deletions django/contrib/contenttypes/management/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import warnings

from django.apps import apps as global_apps
from django.db import DEFAULT_DB_ALIAS, IntegrityError, migrations, router, transaction

Expand Down Expand Up @@ -28,8 +30,14 @@ def _rename(self, apps, schema_editor, old_model, new_model):
content_type.save(using=db, update_fields={"model"})
except IntegrityError:
# Gracefully fallback if a stale content type causes a
# conflict as remove_stale_contenttypes will take care of
# asking the user what should be done next.
# conflict. Warn the user so they can run the
# remove_stale_contenttypes management command.
warnings.warn(
f"Could not rename content type '{self.app_label}.{old_model}' "
f"to '{new_model}' due to an existing conflicting content type. "
"Run 'remove_stale_contenttypes' to clean up stale entries.",
RuntimeWarning,
)
content_type.model = old_model
else:
# Clear the cache as the `get_by_natural_key()` call will cache
Expand Down
6 changes: 0 additions & 6 deletions django/utils/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,8 @@ def process(self, data):
class Truncator(SimpleLazyObject):
"""
An object used to truncate text, either by characters or words.
When truncating HTML text (either chars or words), input will be limited to
at most `MAX_LENGTH_HTML` characters.
"""

# 5 million characters are approximately 4000 text pages or 3 web pages.
MAX_LENGTH_HTML = 5_000_000

def __init__(self, text):
super().__init__(lambda: str(text))

Expand Down
6 changes: 2 additions & 4 deletions docs/ref/templates/builtins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2860,8 +2860,7 @@ Newlines in the HTML content will be preserved.
.. admonition:: Size of input string

Processing large, potentially malformed HTML strings can be
resource-intensive and impact service performance. ``truncatechars_html``
limits input to the first five million characters.
resource-intensive and impact service performance.

.. templatefilter:: truncatewords

Expand Down Expand Up @@ -2908,8 +2907,7 @@ Newlines in the HTML content will be preserved.
.. admonition:: Size of input string

Processing large, potentially malformed HTML strings can be
resource-intensive and impact service performance. ``truncatewords_html``
limits input to the first five million characters.
resource-intensive and impact service performance.

.. templatefilter:: unordered_list

Expand Down
42 changes: 27 additions & 15 deletions tests/contenttypes_tests/test_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,19 @@ def test_missing_content_type_rename_ignore(self):
def test_content_type_rename_conflict(self):
ContentType.objects.create(app_label="contenttypes_tests", model="foo")
ContentType.objects.create(app_label="contenttypes_tests", model="renamedfoo")
call_command(
"migrate",
"contenttypes_tests",
database="default",
interactive=False,
verbosity=0,
)
msg = (
"Could not rename content type 'contenttypes_tests.foo' to "
"'renamedfoo' due to an existing conflicting content type. "
"Run 'remove_stale_contenttypes' to clean up stale entries."
)
with self.assertWarnsMessage(RuntimeWarning, msg):
call_command(
"migrate",
"contenttypes_tests",
database="default",
interactive=False,
verbosity=0,
)
self.assertTrue(
ContentType.objects.filter(
app_label="contenttypes_tests", model="foo"
Expand All @@ -171,14 +177,20 @@ def test_content_type_rename_conflict(self):
app_label="contenttypes_tests", model="renamedfoo"
).exists()
)
call_command(
"migrate",
"contenttypes_tests",
"zero",
database="default",
interactive=False,
verbosity=0,
)
msg = (
"Could not rename content type 'contenttypes_tests.renamedfoo' to "
"'foo' due to an existing conflicting content type. "
"Run 'remove_stale_contenttypes' to clean up stale entries."
)
with self.assertWarnsMessage(RuntimeWarning, msg):
call_command(
"migrate",
"contenttypes_tests",
"zero",
database="default",
interactive=False,
verbosity=0,
)
self.assertTrue(
ContentType.objects.filter(
app_label="contenttypes_tests", model="foo"
Expand Down
36 changes: 0 additions & 36 deletions tests/utils_tests/test_text.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
import sys
from unittest.mock import patch

from django.core.exceptions import SuspiciousFileOperation
from django.test import SimpleTestCase
Expand Down Expand Up @@ -136,23 +135,6 @@ def test_truncate_chars_html(self):
truncator = text.Truncator("foo</p>")
self.assertEqual("foo</p>", truncator.chars(5, html=True))

@patch("django.utils.text.Truncator.MAX_LENGTH_HTML", 10_000)
def test_truncate_chars_html_size_limit(self):
max_len = text.Truncator.MAX_LENGTH_HTML
bigger_len = text.Truncator.MAX_LENGTH_HTML + 1
valid_html = "<p>Joel is a slug</p>" # 14 chars
perf_test_values = [
("</a" + "\t" * (max_len - 6) + "//>", "</a>"),
("</p" + "\t" * bigger_len + "//>", "</p>"),
("&" * bigger_len, ""),
("_X<<<<<<<<<<<>", "_X&lt;&lt;&lt;&lt;&lt;&lt;&lt;…"),
(valid_html * bigger_len, "<p>Joel is a…</p>"), # 10 chars
]
for value, expected in perf_test_values:
with self.subTest(value=value):
truncator = text.Truncator(value)
self.assertEqual(expected, truncator.chars(10, html=True))

def test_truncate_chars_html_with_newline_inside_tag(self):
truncator = text.Truncator(
'<p>The quick <a href="xyz.html"\n id="mylink">brown fox</a> jumped over '
Expand Down Expand Up @@ -329,24 +311,6 @@ def test_truncate_html_words(self):
self.assertEqual(truncator.words(3, html=True), "hello &gt;&lt;…")
self.assertEqual(truncator.words(4, html=True), "hello &gt;&lt; world")

@patch("django.utils.text.Truncator.MAX_LENGTH_HTML", 10_000)
def test_truncate_words_html_size_limit(self):
max_len = text.Truncator.MAX_LENGTH_HTML
bigger_len = text.Truncator.MAX_LENGTH_HTML + 1
valid_html = "<p>Joel is a slug</p>" # 4 words
perf_test_values = [
("</a" + "\t" * (max_len - 6) + "//>", "</a>"),
("</p" + "\t" * bigger_len + "//>", "</p>"),
("&" * max_len, ""),
("&" * bigger_len, ""),
("_X<<<<<<<<<<<>", "_X&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&gt;"),
(valid_html * bigger_len, valid_html * 12 + "<p>Joel is…</p>"), # 50 words
]
for value, expected in perf_test_values:
with self.subTest(value=value):
truncator = text.Truncator(value)
self.assertEqual(expected, truncator.words(50, html=True))

def test_wrap(self):
digits = "1234 67 9"
self.assertEqual(text.wrap(digits, 100), "1234 67 9")
Expand Down
Loading