Ticket #7028: ticket7028-7.patch
File ticket7028-7.patch, 22.9 KB (added by , 15 years ago) |
---|
-
AUTHORS
diff --git a/AUTHORS b/AUTHORS index 55211a6..d3ad681 100644
a b answer newbie questions, and generally made Django that much better: 69 69 Esdras Beleza <linux@esdrasbeleza.com> 70 70 Chris Bennett <chrisrbennett@yahoo.com> 71 71 James Bennett 72 Marco Beri <marcoberi@gmail.com> 72 73 Julian Bez 73 74 Arvis Bickovskis <viestards.lists@gmail.com> 74 75 Natalia Bidart -
django/contrib/admin/media/js/admin/RelatedObjectLookups.js
diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js index 1bc78f8..a954eb8 100644
a b function html_unescape(text) { 11 11 return text; 12 12 } 13 13 14 function html_escape(text) { 15 text = text.replace(/&/g, '&'); 16 text = text.replace(/</g, '<'); 17 text = text.replace(/>/g, '>'); 18 text = text.replace(/"/g, '"'); 19 text = text.replace(/'/g, '''); 20 return text; 21 } 22 14 23 // IE doesn't accept periods or dashes in the window name, but the element IDs 15 24 // we use to generate popup window names may contain them, therefore we map them 16 // to allowed characters in a reversible way so that we can locate the correct 25 // to allowed characters in a reversible way so that we can locate the correct 17 26 // element when the popup window is dismissed. 18 27 function id_to_windowname(text) { 19 28 text = text.replace(/\./g, '__dot__'); … … function windowname_to_id(text) { 27 36 return text; 28 37 } 29 38 39 function getAdminMediaPrefix() { 40 // Deduce admin_media_prefix by looking at the <script>s in the 41 // current document and finding the URL of *this* module. 42 // Copy-paste from DateTimeShortcuts.js, makes sense as a 43 // separate function in core.js. 44 var scripts = document.getElementsByTagName('script'); 45 46 for (var i=0; i < scripts.length; i++) { 47 if (scripts[i].src.match(/RelatedObjectLookups/)) { 48 var idx = scripts[i].src.indexOf('js/admin/RelatedObjectLookups'); 49 return scripts[i].src.substring(0, idx); 50 } 51 } 52 // poor man's assert 53 alert('This line is unreachable. Please file a bug if you see this message.'); 54 } 55 56 var CLEAR_RAW_ID = '<a href="#" onclick="return clearRawId(this);">' + 57 '<img src="' + getAdminMediaPrefix() + 'img/admin/icon_deletelink.gif" ' + 58 'width="10" height="10" alt="Clear" title="Clear" /></a>'; 59 60 // FIXME: the following produce 'gettext is not defined' errors in FireBug. 61 // Needs to be tracked down. 62 // (jsi18n is generally included before this in admin templates) 63 // 64 // 'width="10" height="10" alt="' + gettext('Clear') + '" title="' + 65 // gettext('Clear') + '" /></a>'; 66 67 function showRelatedObjectPopup(triggeringLink) { 68 var name = triggeringLink.parentNode.id.replace(/^view_lookup_/, ''); 69 name = id_to_windowname(name); 70 return openPopupWindow(triggeringLink.href, '_popup', name); 71 } 72 30 73 function showRelatedObjectLookupPopup(triggeringLink) { 31 74 var name = triggeringLink.id.replace(/^lookup_/, ''); 32 75 name = id_to_windowname(name); 33 var href; 34 if (triggeringLink.href.search(/\?/) >= 0) { 35 href = triggeringLink.href + '&pop=1'; 36 } else { 37 href = triggeringLink.href + '?pop=1'; 38 } 39 var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 40 win.focus(); 41 return false; 76 return openPopupWindow(triggeringLink.href, 'pop', name); 42 77 } 43 78 44 function dismissRelatedLookupPopup(win, chosenId ) {79 function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) { 45 80 var name = windowname_to_id(win.name); 46 81 var elem = document.getElementById(name); 82 var nameElem = document.getElementById("view_lookup_" + name); 83 47 84 if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 48 85 elem.value += ',' + chosenId; 49 86 } else { 50 87 document.getElementById(name).value = chosenId; 51 88 } 89 90 if (nameElem) { 91 nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 92 'onclick="return showRelatedObjectPopup(this);">' + 93 html_escape(chosenName) + '</a> ' + CLEAR_RAW_ID; 94 } 95 52 96 win.close(); 53 97 } 54 98 55 99 function showAddAnotherPopup(triggeringLink) { 56 100 var name = triggeringLink.id.replace(/^add_/, ''); 57 101 name = id_to_windowname(name); 58 href = triggeringLink.href 59 if (href.indexOf('?') == -1) { 60 href += '?_popup=1'; 61 } else { 62 href += '&_popup=1'; 63 } 64 var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 65 win.focus(); 66 return false; 102 return openPopupWindow(triggeringLink.href, '_popup', name); 67 103 } 68 104 69 105 function dismissAddAnotherPopup(win, newId, newRepr) { 70 106 // newId and newRepr are expected to have previously been escaped by 71 107 // django.utils.html.escape. 72 108 newId = html_unescape(newId); 109 var newRepr_escaped = newRepr; 73 110 newRepr = html_unescape(newRepr); 74 111 var name = windowname_to_id(win.name); 75 112 var elem = document.getElementById(name); … … function dismissAddAnotherPopup(win, newId, newRepr) { 84 121 } else { 85 122 elem.value = newId; 86 123 } 124 125 var nameElem = document.getElementById("view_lookup_" + name); 126 if (nameElem) { 127 var chosenIdHref = win.location.href.replace(/\/add\/[^\/]*$/, 128 '/' + newId + '/'); 129 nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' + 130 'onclick="return showRelatedObjectPopup(this);">' + 131 newRepr_escaped + '</a> ' + CLEAR_RAW_ID; 132 } 87 133 } 88 134 } else { 89 135 var toId = name + "_to"; … … function dismissAddAnotherPopup(win, newId, newRepr) { 94 140 } 95 141 win.close(); 96 142 } 143 144 function clearRawId(triggeringLink) { 145 triggeringLink.parentNode.previousSibling.previousSibling.previousSibling.previousSibling.value = ''; 146 triggeringLink.parentNode.innerHTML = ''; 147 return false; 148 } 149 150 function openPopupWindow(href, popup_var, name) { 151 if (href.indexOf('?') == -1) { 152 href += '?'; 153 } else { 154 href += '&'; 155 } 156 href += popup_var + '=1'; 157 var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 158 win.focus(); 159 return false; 160 } -
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 8f68eb1..140b79c 100644
a b from django.forms.models import BaseInlineFormSet 5 5 from django.contrib.contenttypes.models import ContentType 6 6 from django.contrib.admin import widgets 7 7 from django.contrib.admin import helpers 8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict 8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict, obj_label, get_related_url 9 9 from django.contrib import messages 10 10 from django.views.decorators.csrf import csrf_protect 11 11 from django.core.exceptions import PermissionDenied, ValidationError … … class ModelAdmin(BaseModelAdmin): 674 674 return HttpResponseRedirect(request.path + "?_popup=1") 675 675 else: 676 676 return HttpResponseRedirect(request.path) 677 elif request.POST.has_key("_popup"): 678 # object changed via raw id link popup 679 obj_id = repr(force_unicode(obj._get_pk_val()))[1:] 680 obj_url = get_related_url(obj, obj.pk) 681 label = obj_label(obj).replace("'", r"\'") 682 return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup(' 683 "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label)) 677 684 elif request.POST.has_key("_saveasnew"): 678 685 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 679 686 self.message_user(request, msg) -
django/contrib/admin/templatetags/admin_list.py
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 7cd3f0a..bf8fba2 100644
a b from django.conf import settings 4 4 from django.contrib.admin.util import lookup_field, display_for_field, label_for_field 5 5 from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE 6 6 from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR 7 from django.contrib.admin.util import obj_label, get_related_url 7 8 from django.core.exceptions import ObjectDoesNotExist 8 9 from django.db import models 9 10 from django.forms.forms import pretty_name … … def items_for_result(cl, result, form): 166 167 attr = pk 167 168 value = result.serializable_value(attr) 168 169 result_id = repr(force_unicode(value))[1:] 169 yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 170 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag)) 170 result_name = obj_label(result) 171 result_url = get_related_url(result, result.pk) 172 yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % 173 (table_tag, row_class, url, 174 (cl.is_popup 175 and ' onclick="opener.dismissRelatedLookupPopup(' 176 "window, %s, '%s', '%s'); return false;\"" % 177 (result_id, result_url, 178 result_name.replace("'", r"\'")) 179 or ''), 180 conditional_escape(result_repr), table_tag)) 171 181 else: 172 182 # By default the fields come from ModelAdmin.list_editable, but if we pull 173 183 # the fields out of the form instead of list_editable custom admins -
django/contrib/admin/util.py
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py index d252a80..dbaea60 100644
a b from django.db import models 3 3 from django.utils import formats 4 4 from django.utils.html import escape 5 5 from django.utils.safestring import mark_safe 6 from django.utils.text import capfirst 6 from django.utils.text import capfirst, truncate_words 7 7 from django.utils.encoding import force_unicode, smart_unicode, smart_str 8 8 from django.utils.translation import ungettext, ugettext as _ 9 9 from django.core.urlresolvers import reverse, NoReverseMatch … … def display_for_field(value, field): 305 305 return formats.number_format(value) 306 306 else: 307 307 return smart_unicode(value) 308 309 def get_related_url(rel_to, action=None, admin_site=None): 310 reverse_path = 'admin:%s_%s' 311 rel_path = '%s%s/%s/' 312 params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()] 313 314 if action: 315 reverse_path += '_%s' 316 rel_path += '%s/' 317 params.append(action) 318 319 if admin_site: 320 try: 321 return reverse(reverse_path % tuple(params), 322 current_app=admin_site.name) 323 except NoReverseMatch: 324 params.insert(0, admin_site.root_path) 325 return rel_path % tuple(params) 326 327 params.insert(0, '../../../') 328 return rel_path % tuple(params) 329 330 def obj_label(obj): 331 return escape(truncate_words(obj, 7)) -
django/contrib/admin/widgets.py
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 120df94..2a788ee 100644
a b import django.utils.copycompat as copy 7 7 from django import forms 8 8 from django.forms.widgets import RadioFieldRenderer 9 9 from django.forms.util import flatatt 10 from django.utils.html import escape11 from django.utils.text import truncate_words12 10 from django.utils.translation import ugettext as _ 13 11 from django.utils.safestring import mark_safe 14 12 from django.utils.encoding import force_unicode 15 13 from django.conf import settings 16 from django.co re.urlresolvers import reverse, NoReverseMatch14 from django.contrib.admin.util import get_related_url, obj_label 17 15 18 16 class FilteredSelectMultiple(forms.SelectMultiple): 19 17 """ … … class ForeignKeyRawIdWidget(forms.TextInput): 110 108 def render(self, name, value, attrs=None): 111 109 if attrs is None: 112 110 attrs = {} 113 related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())111 related_url = get_related_url(self.rel.to) 114 112 params = self.url_parameters() 115 113 if params: 116 114 url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) … … class ForeignKeyRawIdWidget(forms.TextInput): 121 119 output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] 122 120 # TODO: "id_" is hard-coded here. This should instead use the correct 123 121 # API to determine the ID dynamically. 124 output.append(' <a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \122 output.append(' <a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ 125 123 (related_url, url, name)) 126 output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) 127 if value: 128 output.append(self.label_for_value(value)) 124 output.append('<img src="%(prefix)simg/admin/selector-search.gif" width="16" height="16" alt="%(title)s" title="%(title)s" /></a>' % {'prefix': settings.ADMIN_MEDIA_PREFIX, 'title': _('Lookup')}) 125 output.append(self.label_for_value(value, 'view_lookup_id_%s' % name)) 129 126 return mark_safe(u''.join(output)) 130 127 131 128 def base_url_parameters(self): … … class ForeignKeyRawIdWidget(forms.TextInput): 147 144 params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) 148 145 return params 149 146 150 def label_for_value(self, value): 151 key = self.rel.get_related_field().name 152 obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) 153 return ' <strong>%s</strong>' % escape(truncate_words(obj, 14)) 147 def label_for_value(self, value, name): 148 if value: 149 key = self.rel.get_related_field().name 150 obj = self.rel.to._default_manager.using( 151 self.db).get(**{key: value}) 152 related_url = get_related_url(obj, obj.pk) 153 return (' <strong id="%(name)s"><a href="%(url)s" ' 154 'onclick="return showRelatedObjectPopup(this);">%(label)s</a> ' 155 '<a href="#" onclick="return clearRawId(this);">' 156 '<img src="%(prefix)simg/admin/icon_deletelink.gif" ' 157 'width="10" height="10" alt="%(clear)s" title="%(clear)s" />' 158 '</a></strong>' % {'name': name, 'url': related_url, 159 'label': obj_label(obj), 160 'prefix': settings.ADMIN_MEDIA_PREFIX, 161 'clear': _("Clear"),} 162 ) 163 else: 164 # a placeholder that will be filled in 165 # JavaScript dismissRelatedLookupPopup() 166 return ' <strong id="%s"></strong>' % name 154 167 155 168 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 156 169 """ … … class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 171 184 def url_parameters(self): 172 185 return self.base_url_parameters() 173 186 174 def label_for_value(self, value ):187 def label_for_value(self, value, name): 175 188 return '' 176 189 177 190 def value_from_datadict(self, data, files, name): … … class RelatedFieldWidgetWrapper(forms.Widget): 221 234 media = property(_media) 222 235 223 236 def render(self, name, value, *args, **kwargs): 224 rel_to = self.rel.to225 info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())226 try:227 related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)228 except NoReverseMatch:229 info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())230 related_url = '%s%s/%s/add/' % info231 237 self.widget.choices = self.choices 232 238 output = [self.widget.render(name, value, *args, **kwargs)] 239 rel_to = self.rel.to 233 240 if rel_to in self.admin_site._registry: # If the related object has an admin interface: 241 related_url = get_related_url(rel_to, 'add', self.admin_site) 234 242 # TODO: "id_" is hard-coded here. This should instead use the correct 235 243 # API to determine the ID dynamically. 236 244 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ -
tests/regressiontests/admin_widgets/models.py
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py index b9fe67c..e997e1a 100644
a b from django.db import models 4 4 from django.core.files.storage import default_storage 5 5 from django.contrib.auth.models import User 6 6 7 class MyFileField(models.FileField): 8 pass 7 class MyFileField(models.FileField): 8 pass 9 9 10 10 class Member(models.Model): 11 11 name = models.CharField(max_length=100) … … Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb 116 116 >>> rel = Album._meta.get_field('band').rel 117 117 >>> w = ForeignKeyRawIdWidget(rel) 118 118 >>> print conditional_escape(w.render('test', band.pk, attrs={})) 119 <input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Linkin Park</strong>119 <input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/" onclick="return showRelatedObjectPopup(this);">Linkin Park</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 120 120 121 121 >>> m1 = Member.objects.create(pk=1, name='Chester') 122 122 >>> m2 = Member.objects.create(pk=2, name='Mike') … … Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb 125 125 >>> rel = Band._meta.get_field('members').rel 126 126 >>> w = ManyToManyRawIdWidget(rel) 127 127 >>> print conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})) 128 <input type="text" name="test" value="1,2" class="vManyToManyRawIdAdminField" /><a href="../../../admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a> 128 ||| FIXME -- ManyToManyRawIdWidget has not been implemented ||| 129 129 >>> w._has_changed(None, None) 130 130 False 131 131 >>> w._has_changed([], None) … … True 147 147 >>> rel = Inventory._meta.get_field('parent').rel 148 148 >>> w = ForeignKeyRawIdWidget(rel) 149 149 >>> print w.render('test', core.parent_id, attrs={}) 150 <input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Apple</strong>150 <input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Apple</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 151 151 152 152 # see #9258 153 153 >>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True) 154 154 >>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden) 155 155 >>> print w.render('test', child_of_hidden.parent_id, attrs={}) 156 <input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Hidden</strong> 156 <input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/" onclick="return showRelatedObjectPopup(this);">Hidden</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 157 158 >>> apostrophe = Inventory.objects.create(barcode=88, name="'Apostrophe', and <javascript> and a really really` really really really really really long one!") 159 >>> apostrophic_child = Inventory.objects.create(barcode=89, name="Apostrophe's child", parent=apostrophe) 160 >>> print w.render('test', apostrophic_child.parent_id, attrs={}) 161 <input type="text" name="test" value="88" class="vForeignKeyRawIdAdminField" /> <a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" title="Lookup" /></a> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/6/" onclick="return showRelatedObjectPopup(this);">'Apostrophe', and <javascript> and a really really` ...</a> <a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/admin/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong> 157 162 """ % { 158 163 'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, 159 164 'STORAGE_URL': default_storage.url(''),