Fixed #22894 -- Made admin add related/raw ID fields widgets customizable.
Decoupled behavior and style from the RelatedFieldWidgetWrapper and ForeignKeyRawIdWidget.
This commit is contained in:
parent
ec85df2fa5
commit
68c9a72e29
|
@ -374,3 +374,22 @@ body.popup .submit-row {
|
||||||
.empty-form {
|
.empty-form {
|
||||||
display: none;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -27,20 +27,24 @@ function windowname_to_id(text) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRelatedObjectLookupPopup(triggeringLink) {
|
function showAdminPopup(triggeringLink, name_regexp) {
|
||||||
var name = triggeringLink.id.replace(/^lookup_/, '');
|
var name = triggeringLink.id.replace(name_regexp, '');
|
||||||
name = id_to_windowname(name);
|
name = id_to_windowname(name);
|
||||||
var href;
|
var href = triggeringLink.href;
|
||||||
if (triggeringLink.href.search(/\?/) >= 0) {
|
if (href.indexOf('?') == -1) {
|
||||||
href = triggeringLink.href + '&_popup=1';
|
href += '?_popup=1';
|
||||||
} else {
|
} else {
|
||||||
href = triggeringLink.href + '?_popup=1';
|
href += '&_popup=1';
|
||||||
}
|
}
|
||||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
win.focus();
|
win.focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showRelatedObjectLookupPopup(triggeringLink) {
|
||||||
|
return showAdminPopup(triggeringLink, /^lookup_/);
|
||||||
|
}
|
||||||
|
|
||||||
function dismissRelatedLookupPopup(win, chosenId) {
|
function dismissRelatedLookupPopup(win, chosenId) {
|
||||||
var name = windowname_to_id(win.name);
|
var name = windowname_to_id(win.name);
|
||||||
var elem = document.getElementById(name);
|
var elem = document.getElementById(name);
|
||||||
|
@ -53,17 +57,7 @@ function dismissRelatedLookupPopup(win, chosenId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAddAnotherPopup(triggeringLink) {
|
function showAddAnotherPopup(triggeringLink) {
|
||||||
var name = triggeringLink.id.replace(/^add_/, '');
|
return showAdminPopup(triggeringLink, /^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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissAddAnotherPopup(win, newId, newRepr) {
|
function dismissAddAnotherPopup(win, newId, newRepr) {
|
||||||
|
|
|
@ -67,15 +67,25 @@
|
||||||
|
|
||||||
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
|
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
|
||||||
|
|
||||||
{% if adminform and add %}
|
{% block admin_change_form_document_ready %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$('.add-another').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
showAddAnotherPopup(this);
|
||||||
|
});
|
||||||
|
$('.related-lookup').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
showRelatedObjectLookupPopup(this);
|
||||||
|
});
|
||||||
|
{% if adminform and add %}
|
||||||
$('form#{{ opts.model_name }}_form :input:visible:enabled:first').focus()
|
$('form#{{ opts.model_name }}_form :input:visible:enabled:first').focus()
|
||||||
|
{% endif %}
|
||||||
});
|
});
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endblock %}
|
||||||
|
|
||||||
{# JavaScript for prepopulated fields #}
|
{# JavaScript for prepopulated fields #}
|
||||||
{% prepopulated_fields_js %}
|
{% prepopulated_fields_js %}
|
||||||
|
|
|
@ -170,10 +170,8 @@ class ForeignKeyRawIdWidget(forms.TextInput):
|
||||||
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
|
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
|
||||||
# TODO: "lookup_id_" is hard-coded here. This should instead use
|
# TODO: "lookup_id_" is hard-coded here. This should instead use
|
||||||
# the correct API to determine the ID dynamically.
|
# the correct API to determine the ID dynamically.
|
||||||
extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' %
|
extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" title="%s"></a>' %
|
||||||
(related_url, url, name))
|
(related_url, url, name, _('Lookup')))
|
||||||
extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>' %
|
|
||||||
(static('admin/img/selector-search.gif'), _('Lookup')))
|
|
||||||
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
|
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
|
||||||
if value:
|
if value:
|
||||||
output.append(self.label_for_value(value))
|
output.append(self.label_for_value(value))
|
||||||
|
@ -265,19 +263,17 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
||||||
|
|
||||||
def render(self, name, value, *args, **kwargs):
|
def render(self, name, value, *args, **kwargs):
|
||||||
from django.contrib.admin.views.main import TO_FIELD_VAR
|
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
|
self.widget.choices = self.choices
|
||||||
output = [self.widget.render(name, value, *args, **kwargs)]
|
output = [self.widget.render(name, value, *args, **kwargs)]
|
||||||
if self.can_add_related:
|
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)
|
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)
|
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
|
# TODO: "add_id_" is hard-coded here. This should instead use the
|
||||||
# correct API to determine the ID dynamically.
|
# correct API to determine the ID dynamically.
|
||||||
output.append('<a href="%s%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
|
output.append('<a href="%s%s" class="add-another" id="add_id_%s" title="%s"></a>'
|
||||||
% (related_url, url_params, name))
|
% (related_url, url_params, name, _('Add Another')))
|
||||||
output.append('<img src="%s" width="10" height="10" alt="%s"/></a>'
|
|
||||||
% (static('admin/img/icon_addlink.gif'), _('Add Another')))
|
|
||||||
return mark_safe(''.join(output))
|
return mark_safe(''.join(output))
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
|
|
|
@ -3990,7 +3990,7 @@ class UserAdminTest(TestCase):
|
||||||
response = self.client.get('/test_admin/admin/admin_views/album/add/')
|
response = self.client.get('/test_admin/admin/admin_views/album/add/')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, '/test_admin/admin/auth/user/add')
|
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')
|
response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertNotContains(response, 'name="_continue"')
|
self.assertNotContains(response, 'name="_continue"')
|
||||||
|
|
|
@ -386,8 +386,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
|
|
||||||
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
w.render('test', band.pk, attrs={}),
|
w.render('test', band.pk, attrs={}), (
|
||||||
'<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/admin_widgets/band/?_to_field=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Linkin Park</strong>' % dict(admin_static_prefix(), bandpk=band.pk)
|
'<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" />'
|
||||||
|
'<a href="/admin_widgets/band/?_to_field=id" class="related-lookup" id="lookup_id_test" title="Lookup"></a>'
|
||||||
|
' <strong>Linkin Park</strong>'
|
||||||
|
) % {'bandpk': band.pk}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_relations_to_non_primary_key(self):
|
def test_relations_to_non_primary_key(self):
|
||||||
|
@ -401,8 +404,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
rel = models.Inventory._meta.get_field('parent').rel
|
rel = models.Inventory._meta.get_field('parent').rel
|
||||||
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
w.render('test', core.parent_id, attrs={}),
|
w.render('test', core.parent_id, attrs={}), (
|
||||||
'<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/admin_widgets/inventory/?_to_field=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Apple</strong>' % admin_static_prefix()
|
'<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" />'
|
||||||
|
'<a href="/admin_widgets/inventory/?_to_field=barcode" class="related-lookup" id="lookup_id_test" title="Lookup">'
|
||||||
|
'</a> <strong>Apple</strong>'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_fk_related_model_not_in_admin(self):
|
def test_fk_related_model_not_in_admin(self):
|
||||||
|
@ -443,8 +449,11 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
barcode=94, name='Child of hidden', parent=hidden
|
barcode=94, name='Child of hidden', parent=hidden
|
||||||
)
|
)
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
w.render('test', child_of_hidden.parent_id, attrs={}),
|
w.render('test', child_of_hidden.parent_id, attrs={}), (
|
||||||
'<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/admin_widgets/inventory/?_to_field=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Hidden</strong>' % admin_static_prefix()
|
'<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" />'
|
||||||
|
'<a href="/admin_widgets/inventory/?_to_field=barcode" class="related-lookup" id="lookup_id_test" title="Lookup">'
|
||||||
|
'</a> <strong>Hidden</strong>'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -461,13 +470,17 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
|
||||||
|
|
||||||
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
|
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
w.render('test', [m1.pk, m2.pk], attrs={}),
|
w.render('test', [m1.pk, m2.pk], attrs={}), (
|
||||||
'<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" /><a href="/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="/static/admin/img/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_static_prefix(), m1pk=m1.pk, m2pk=m2.pk)
|
'<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" />'
|
||||||
|
'<a href="/admin_widgets/member/" class="related-lookup" id="lookup_id_test" title="Lookup"></a>'
|
||||||
|
) % dict(m1pk=m1.pk, m2pk=m2.pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
w.render('test', [m1.pk]),
|
w.render('test', [m1.pk]), (
|
||||||
'<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField" /><a href="/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_static_prefix(), m1pk=m1.pk)
|
'<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField">'
|
||||||
|
'<a href="/admin_widgets/member/" class="related-lookup" id="lookup_id_test" title="Lookup"></a>'
|
||||||
|
) % dict(m1pk=m1.pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_m2m_related_model_not_in_admin(self):
|
def test_m2m_related_model_not_in_admin(self):
|
||||||
|
|
Loading…
Reference in New Issue