diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 0f617b3fb9..95cbe634b2 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -1,23 +1,22 @@ from __future__ import unicode_literals -from collections import defaultdict import datetime import decimal +from collections import defaultdict from django.contrib.auth import get_permission_codename from django.core.exceptions import FieldDoesNotExist +from django.core.urlresolvers import reverse, NoReverseMatch from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector from django.forms.forms import pretty_name -from django.utils import formats -from django.utils.html import format_html -from django.utils.text import capfirst -from django.utils import timezone +from django.utils import formats, six, timezone from django.utils.encoding import force_str, force_text, smart_text -from django.utils import six +from django.utils.html import conditional_escape, format_html +from django.utils.safestring import mark_safe +from django.utils.text import capfirst from django.utils.translation import ungettext -from django.core.urlresolvers import reverse, NoReverseMatch def lookup_needs_distinct(opts, lookup_path): @@ -389,6 +388,11 @@ def display_for_field(value, field): return formats.number_format(value, field.decimal_places) elif isinstance(field, models.FloatField): return formats.number_format(value) + elif isinstance(field, models.FileField): + return mark_safe('%s' % ( + conditional_escape(value.url), + conditional_escape(value), + )) else: return smart_text(value) diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index c2855bb237..37ca50794c 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -350,26 +350,55 @@ class AdminURLWidgetTest(DjangoTestCase): ) -class AdminFileWidgetTest(DjangoTestCase): - def test_render(self): +@override_settings( + PASSWORD_HASHERS=['django.contrib.auth.hashers.SHA1PasswordHasher'], + ROOT_URLCONF='admin_widgets.urls', +) +class AdminFileWidgetTests(DjangoTestCase): + fixtures = ['admin-widgets-users.xml'] + + def setUp(self): band = models.Band.objects.create(name='Linkin Park') - album = band.album_set.create( + self.album = band.album_set.create( name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg' ) + def test_render(self): w = widgets.AdminFileWidget() self.assertHTMLEqual( - w.render('test', album.cover_art), - '

Currently: albums\hybrid_theory.jpg
Change:

' % { - 'STORAGE_URL': default_storage.url('') + w.render('test', self.album.cover_art), + '

Currently: albums\hybrid_theory.jpg ' + '' + ' ' + '
' + 'Change:

' % { + 'STORAGE_URL': default_storage.url(''), }, ) - self.assertHTMLEqual( w.render('test', SimpleUploadedFile('test', b'content')), '', ) + def test_readonly_fields(self): + """ + File widgets should render as a link when they're marked "read only." + """ + self.client.login(username="super", password="secret") + response = self.client.get('/admin_widgets/album/%s/' % self.album.id) + self.assertContains( + response, + '

' + 'albums\hybrid_theory.jpg

' % {'STORAGE_URL': default_storage.url('')}, + html=True, + ) + self.assertNotContains( + response, + '', + html=True, + ) + @override_settings(ROOT_URLCONF='admin_widgets.urls') class ForeignKeyRawIdWidgetTest(DjangoTestCase): diff --git a/tests/admin_widgets/widgetadmin.py b/tests/admin_widgets/widgetadmin.py index fa46530037..6b205b70dc 100644 --- a/tests/admin_widgets/widgetadmin.py +++ b/tests/admin_widgets/widgetadmin.py @@ -24,6 +24,11 @@ class EventAdmin(admin.ModelAdmin): raw_id_fields = ['main_band', 'supporting_bands'] +class AlbumAdmin(admin.ModelAdmin): + fields = ('name', 'cover_art',) + readonly_fields = ('cover_art',) + + class SchoolAdmin(admin.ModelAdmin): filter_vertical = ('students',) filter_horizontal = ('alumni',) @@ -37,7 +42,7 @@ site.register(models.CarTire, CarTireAdmin) site.register(models.Member) site.register(models.Band) site.register(models.Event, EventAdmin) -site.register(models.Album) +site.register(models.Album, AlbumAdmin) site.register(models.Inventory)