diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index e50939b2c1..1b6a0ff8ad 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -207,6 +207,7 @@ class AdminReadonlyField(object):
result_repr = ", ".join(map(six.text_type, value.all()))
else:
result_repr = display_for_field(value, f)
+ result_repr = linebreaksbr(result_repr)
return conditional_escape(result_repr)
diff --git a/docs/releases/1.8.8.txt b/docs/releases/1.8.8.txt
index f3c4e8bba1..8caf5f7ff5 100644
--- a/docs/releases/1.8.8.txt
+++ b/docs/releases/1.8.8.txt
@@ -48,3 +48,6 @@ Bugfixes
``filter_horizontal`` and ``filter_vertical`` widgets, which could result
in inadvertent data loss if a user didn't notice that and then submitted the
form (:ticket:`22955`).
+
+* Fixed a regression in the admin which ignored line breaks in read-only fields
+ instead of converting them to ``
`` (:ticket:`25465`).
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 3cd3b6147c..7a6b25b519 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -382,7 +382,7 @@ class LinkInline(admin.TabularInline):
model = Link
extra = 1
- readonly_fields = ("posted", "multiline")
+ readonly_fields = ("posted", "multiline", "readonly_link_content")
def multiline(self, instance):
return "InlineMultiline\ntest\nstring"
@@ -429,7 +429,7 @@ class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'public']
readonly_fields = (
'posted', 'awesomeness_level', 'coolness', 'value',
- 'multiline', 'multiline_html', lambda obj: "foo"
+ 'multiline', 'multiline_html', lambda obj: "foo", 'readonly_content',
)
inlines = [
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 985a2f6cc3..d68c44fd36 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -419,6 +419,7 @@ class Link(models.Model):
)
url = models.URLField()
post = models.ForeignKey("Post")
+ readonly_link_content = models.TextField()
class PrePopulatedPost(models.Model):
@@ -436,6 +437,7 @@ class PrePopulatedSubPost(models.Model):
class Post(models.Model):
title = models.CharField(max_length=100, help_text="Some help text for the title (with unicode ŠĐĆŽćžšđ)")
content = models.TextField(help_text="Some help text for the content (with unicode ŠĐĆŽćžšđ)")
+ readonly_content = models.TextField()
posted = models.DateField(
default=datetime.date.today,
help_text="Some help text for the date (with unicode ŠĐĆŽćžšđ)"
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index d05c299223..a64e4d76d9 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -49,7 +49,7 @@ from .models import (
Chapter, Character, Child, Choice, City, Collector, Color2,
ComplexSortedPerson, CoverLetter, CustomArticle, DooHickey, Employee,
EmptyModel, FancyDoodad, FieldOverridePost, FilteredManager, FooAccount,
- FoodDelivery, FunkyTag, Gallery, Grommet, Inquisition, Language,
+ FoodDelivery, FunkyTag, Gallery, Grommet, Inquisition, Language, Link,
MainPrepopulated, ModelWithStringPrimaryKey, OtherStory, Paper, Parent,
ParentWithDependentChildren, Person, Persona, Picture, Pizza, Plot,
PluggableSearchPerson, Podcast, Post, Promo, Question, RelatedPrepopulated,
@@ -4071,6 +4071,21 @@ class ReadonlyTest(TestCase):
response = self.client.get(reverse('admin:admin_views_post_change', args=(p.pk,)))
self.assertContains(response, "%d amount of cool" % p.pk)
+ def test_readonly_text_field(self):
+ p = Post.objects.create(
+ title="Readonly test", content="test",
+ readonly_content='test\r\n\r\ntest\r\n\r\ntest\r\n\r\ntest',
+ )
+ Link.objects.create(
+ url="http://www.djangoproject.com", post=p,
+ readonly_link_content="test\r\nlink",
+ )
+ response = self.client.get(reverse('admin:admin_views_post_change', args=(p.pk,)))
+ # Checking readonly field.
+ self.assertContains(response, 'test
test
test
test')
+ # Checking readonly field in inline.
+ self.assertContains(response, 'test
link')
+
def test_readonly_post(self):
data = {
"title": "Django Got Readonly Fields",