From 68c9a72e29c929d896a25b3a0f566c09b6723dd4 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 24 Jun 2014 15:07:36 +0200 Subject: [PATCH] Fixed #22894 -- Made admin add related/raw ID fields widgets customizable. Decoupled behavior and style from the RelatedFieldWidgetWrapper and ForeignKeyRawIdWidget. --- .../contrib/admin/static/admin/css/forms.css | 19 +++++++++++ .../admin/js/admin/RelatedObjectLookups.js | 28 +++++++--------- .../admin/templates/admin/change_form.html | 14 ++++++-- django/contrib/admin/widgets.py | 16 ++++----- tests/admin_views/tests.py | 2 +- tests/admin_widgets/tests.py | 33 +++++++++++++------ 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/django/contrib/admin/static/admin/css/forms.css b/django/contrib/admin/static/admin/css/forms.css index d088d8db42..199ab3475e 100644 --- a/django/contrib/admin/static/admin/css/forms.css +++ b/django/contrib/admin/static/admin/css/forms.css @@ -374,3 +374,22 @@ body.popup .submit-row { .empty-form { display: none; } + +/* RELATED FIELD ADD ONE / LOOKUP */ + +.add-another, .related-lookup { + margin-left: 5px; + display: inline-block; +} + +.add-another { + width: 10px; + height: 10px; + background-image: url(../img/icon_addlink.gif); +} + +.related-lookup { + width: 16px; + height: 16px; + background-image: url(../img/selector-search.gif); +} diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index 0d7ca41d94..01580fd833 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -27,20 +27,24 @@ function windowname_to_id(text) { return text; } -function showRelatedObjectLookupPopup(triggeringLink) { - var name = triggeringLink.id.replace(/^lookup_/, ''); +function showAdminPopup(triggeringLink, name_regexp) { + var name = triggeringLink.id.replace(name_regexp, ''); name = id_to_windowname(name); - var href; - if (triggeringLink.href.search(/\?/) >= 0) { - href = triggeringLink.href + '&_popup=1'; + var href = triggeringLink.href; + if (href.indexOf('?') == -1) { + href += '?_popup=1'; } else { - href = triggeringLink.href + '?_popup=1'; + href += '&_popup=1'; } var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); win.focus(); return false; } +function showRelatedObjectLookupPopup(triggeringLink) { + return showAdminPopup(triggeringLink, /^lookup_/); +} + function dismissRelatedLookupPopup(win, chosenId) { var name = windowname_to_id(win.name); var elem = document.getElementById(name); @@ -53,17 +57,7 @@ function dismissRelatedLookupPopup(win, chosenId) { } function showAddAnotherPopup(triggeringLink) { - var name = triggeringLink.id.replace(/^add_/, ''); - name = id_to_windowname(name); - var href = triggeringLink.href; - if (href.indexOf('?') == -1) { - href += '?_popup=1'; - } else { - href += '&_popup=1'; - } - var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); - win.focus(); - return false; + return showAdminPopup(triggeringLink, /^add_/); } function dismissAddAnotherPopup(win, newId, newRepr) { diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index d68f71ebd3..0fc4a25138 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -67,15 +67,25 @@ {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} -{% if adminform and add %} +{% block admin_change_form_document_ready %} -{% endif %} +{% endblock %} {# JavaScript for prepopulated fields #} {% prepopulated_fields_js %} diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 68575794f6..3b14087176 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -170,10 +170,8 @@ class ForeignKeyRawIdWidget(forms.TextInput): attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook. # TODO: "lookup_id_" is hard-coded here. This should instead use # the correct API to determine the ID dynamically. - extra.append(' ' % - (related_url, url, name)) - extra.append('%s' % - (static('admin/img/selector-search.gif'), _('Lookup'))) + extra.append('' % + (related_url, url, name, _('Lookup'))) output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra if value: output.append(self.label_for_value(value)) @@ -265,19 +263,17 @@ class RelatedFieldWidgetWrapper(forms.Widget): def render(self, name, value, *args, **kwargs): from django.contrib.admin.views.main import TO_FIELD_VAR - rel_to = self.rel.to - info = (rel_to._meta.app_label, rel_to._meta.model_name) self.widget.choices = self.choices output = [self.widget.render(name, value, *args, **kwargs)] if self.can_add_related: + rel_to = self.rel.to + info = (rel_to._meta.app_label, rel_to._meta.model_name) related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) url_params = '?%s=%s' % (TO_FIELD_VAR, self.rel.get_related_field().name) # TODO: "add_id_" is hard-coded here. This should instead use the # correct API to determine the ID dynamically. - output.append(' ' - % (related_url, url_params, name)) - output.append('%s' - % (static('admin/img/icon_addlink.gif'), _('Add Another'))) + output.append('' + % (related_url, url_params, name, _('Add Another'))) return mark_safe(''.join(output)) def build_attrs(self, extra_attrs=None, **kwargs): diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index e75b86ac4b..f1d2dab520 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -3990,7 +3990,7 @@ class UserAdminTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/album/add/') self.assertEqual(response.status_code, 200) self.assertContains(response, '/test_admin/admin/auth/user/add') - self.assertContains(response, 'class="add-another" id="add_id_owner" onclick="return showAddAnotherPopup(this);"') + self.assertContains(response, 'class="add-another" id="add_id_owner"') response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1') self.assertEqual(response.status_code, 200) self.assertNotContains(response, 'name="_continue"') diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 35fc04e9f8..39f89f6a40 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -386,8 +386,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - w.render('test', band.pk, attrs={}), - ' Lookup Linkin Park' % dict(admin_static_prefix(), bandpk=band.pk) + w.render('test', band.pk, attrs={}), ( + '' + '' + ' Linkin Park' + ) % {'bandpk': band.pk} ) def test_relations_to_non_primary_key(self): @@ -401,8 +404,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): rel = models.Inventory._meta.get_field('parent').rel w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - w.render('test', core.parent_id, attrs={}), - ' Lookup Apple' % admin_static_prefix() + w.render('test', core.parent_id, attrs={}), ( + '' + '' + ' Apple' + ) ) def test_fk_related_model_not_in_admin(self): @@ -443,8 +449,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): barcode=94, name='Child of hidden', parent=hidden ) self.assertHTMLEqual( - w.render('test', child_of_hidden.parent_id, attrs={}), - ' Lookup Hidden' % admin_static_prefix() + w.render('test', child_of_hidden.parent_id, attrs={}), ( + '' + '' + ' Hidden' + ) ) @@ -461,13 +470,17 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase): w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - w.render('test', [m1.pk, m2.pk], attrs={}), - ' Lookup' % dict(admin_static_prefix(), m1pk=m1.pk, m2pk=m2.pk) + w.render('test', [m1.pk, m2.pk], attrs={}), ( + '' + '' + ) % dict(m1pk=m1.pk, m2pk=m2.pk) ) self.assertHTMLEqual( - w.render('test', [m1.pk]), - ' Lookup' % dict(admin_static_prefix(), m1pk=m1.pk) + w.render('test', [m1.pk]), ( + '' + '' + ) % dict(m1pk=m1.pk) ) def test_m2m_related_model_not_in_admin(self):