Fixed #16542 -- Made Raw ID form widgets shipped with the admin app render the related object lookup tool only when the related model is effectively registered with the AdminSite.
Also, converted these widgets to reverse named URLs instead of hard-coded '../../...'-style links, refs #15294. Thanks Florian Apolloner for the initial patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16578 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
56775c23ee
commit
290d7d4d21
|
@ -154,7 +154,8 @@ class BaseModelAdmin(object):
|
||||||
"""
|
"""
|
||||||
db = kwargs.get('using')
|
db = kwargs.get('using')
|
||||||
if db_field.name in self.raw_id_fields:
|
if db_field.name in self.raw_id_fields:
|
||||||
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, using=db)
|
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel,
|
||||||
|
self.admin_site, using=db)
|
||||||
elif db_field.name in self.radio_fields:
|
elif db_field.name in self.radio_fields:
|
||||||
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
|
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
|
||||||
'class': get_ul_class(self.radio_fields[db_field.name]),
|
'class': get_ul_class(self.radio_fields[db_field.name]),
|
||||||
|
@ -174,7 +175,8 @@ class BaseModelAdmin(object):
|
||||||
db = kwargs.get('using')
|
db = kwargs.get('using')
|
||||||
|
|
||||||
if db_field.name in self.raw_id_fields:
|
if db_field.name in self.raw_id_fields:
|
||||||
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db)
|
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel,
|
||||||
|
self.admin_site, using=db)
|
||||||
kwargs['help_text'] = ''
|
kwargs['help_text'] = ''
|
||||||
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
||||||
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
||||||
|
|
|
@ -4,7 +4,7 @@ Form Widget classes specific to the Django admin site.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms.widgets import RadioFieldRenderer
|
from django.forms.widgets import RadioFieldRenderer
|
||||||
from django.forms.util import flatatt
|
from django.forms.util import flatatt
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
@ -112,29 +112,38 @@ class ForeignKeyRawIdWidget(forms.TextInput):
|
||||||
A Widget for displaying ForeignKeys in the "raw_id" interface rather than
|
A Widget for displaying ForeignKeys in the "raw_id" interface rather than
|
||||||
in a <select> box.
|
in a <select> box.
|
||||||
"""
|
"""
|
||||||
def __init__(self, rel, attrs=None, using=None):
|
def __init__(self, rel, admin_site, attrs=None, using=None):
|
||||||
self.rel = rel
|
self.rel = rel
|
||||||
|
self.admin_site = admin_site
|
||||||
self.db = using
|
self.db = using
|
||||||
super(ForeignKeyRawIdWidget, self).__init__(attrs)
|
super(ForeignKeyRawIdWidget, self).__init__(attrs)
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
|
rel_to = self.rel.to
|
||||||
if attrs is None:
|
if attrs is None:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
|
extra = []
|
||||||
|
if rel_to in self.admin_site._registry:
|
||||||
|
# The related object is registered with the same AdminSite
|
||||||
|
related_url = reverse('admin:%s_%s_changelist' %
|
||||||
|
(rel_to._meta.app_label,
|
||||||
|
rel_to._meta.module_name),
|
||||||
|
current_app=self.admin_site.name)
|
||||||
|
|
||||||
params = self.url_parameters()
|
params = self.url_parameters()
|
||||||
if params:
|
if params:
|
||||||
url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()])
|
url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()])
|
||||||
else:
|
else:
|
||||||
url = u''
|
url = u''
|
||||||
if "class" not in attrs:
|
if "class" not in attrs:
|
||||||
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
|
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
|
||||||
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
|
# TODO: "lookup_id_" is hard-coded here. This should instead use
|
||||||
# TODO: "id_" is hard-coded here. This should instead use the correct
|
# the correct API to determine the ID dynamically.
|
||||||
# API to determine the ID dynamically.
|
extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
|
||||||
output.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
|
|
||||||
% (related_url, url, name))
|
% (related_url, url, name))
|
||||||
output.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
|
extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
|
||||||
% (static('admin/img/selector-search.gif'), _('Lookup')))
|
% (static('admin/img/selector-search.gif'), _('Lookup')))
|
||||||
|
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))
|
||||||
return mark_safe(u''.join(output))
|
return mark_safe(u''.join(output))
|
||||||
|
@ -164,6 +173,8 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs is None:
|
if attrs is None:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
|
if self.rel.to in self.admin_site._registry:
|
||||||
|
# The related object is registered with the same AdminSite
|
||||||
attrs['class'] = 'vManyToManyRawIdAdminField'
|
attrs['class'] = 'vManyToManyRawIdAdminField'
|
||||||
if value:
|
if value:
|
||||||
value = ','.join([force_unicode(v) for v in value])
|
value = ','.join([force_unicode(v) for v in value])
|
||||||
|
@ -232,8 +243,8 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
||||||
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:
|
||||||
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)
|
||||||
# TODO: "id_" is hard-coded here. This should instead use the correct
|
# TODO: "add_id_" is hard-coded here. This should instead use the
|
||||||
# API to determine the ID dynamically.
|
# correct API to determine the ID dynamically.
|
||||||
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
|
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
|
||||||
% (related_url, name))
|
% (related_url, name))
|
||||||
output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
|
output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
|
||||||
|
|
|
@ -67,3 +67,35 @@ class CarTire(models.Model):
|
||||||
A single car tire. This to test that a user can only select their own cars.
|
A single car tire. This to test that a user can only select their own cars.
|
||||||
"""
|
"""
|
||||||
car = models.ForeignKey(Car)
|
car = models.ForeignKey(Car)
|
||||||
|
|
||||||
|
class Honeycomb(models.Model):
|
||||||
|
location = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class Bee(models.Model):
|
||||||
|
"""
|
||||||
|
A model with a FK to a model that won't be registered with the admin
|
||||||
|
(Honeycomb) so the corresponding raw ID widget won't have a magnifying
|
||||||
|
glass link to select related honeycomb instances.
|
||||||
|
"""
|
||||||
|
honeycomb = models.ForeignKey(Honeycomb)
|
||||||
|
|
||||||
|
class Individual(models.Model):
|
||||||
|
"""
|
||||||
|
A model with a FK to itself. It won't be registered with the admin, so the
|
||||||
|
corresponding raw ID widget won't have a magnifying glass link to select
|
||||||
|
related instances (rendering will be called programmatically in this case).
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
parent = models.ForeignKey('self', null=True)
|
||||||
|
|
||||||
|
class Company(models.Model):
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class Advisor(models.Model):
|
||||||
|
"""
|
||||||
|
A model with a m2m to a model that won't be registered with the admin
|
||||||
|
(Company) so the corresponding raw ID widget won't have a magnifying
|
||||||
|
glass link to select related company instances.
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
companies = models.ManyToManyField(Company)
|
||||||
|
|
|
@ -7,10 +7,6 @@ from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.admin import widgets
|
from django.contrib.admin import widgets
|
||||||
from django.contrib.admin.widgets import (FilteredSelectMultiple,
|
|
||||||
AdminSplitDateTime, AdminFileWidget, ForeignKeyRawIdWidget, AdminRadioSelect,
|
|
||||||
RelatedFieldWidgetWrapper, ManyToManyRawIdWidget,
|
|
||||||
url_params_from_lookup_dict)
|
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.db.models import DateField
|
from django.db.models import DateField
|
||||||
|
@ -20,6 +16,7 @@ from django.utils.html import conditional_escape
|
||||||
from django.utils.unittest import TestCase
|
from django.utils.unittest import TestCase
|
||||||
|
|
||||||
import models
|
import models
|
||||||
|
from widgetadmin import site as widget_admin_site
|
||||||
|
|
||||||
admin_media_prefix = lambda: {
|
admin_media_prefix = lambda: {
|
||||||
'ADMIN_MEDIA_PREFIX': "%sadmin/" % settings.STATIC_URL,
|
'ADMIN_MEDIA_PREFIX': "%sadmin/" % settings.STATIC_URL,
|
||||||
|
@ -186,22 +183,22 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase):
|
||||||
'Select a valid choice. That choice is not one of the available choices.')
|
'Select a valid choice. That choice is not one of the available choices.')
|
||||||
|
|
||||||
def test_url_params_from_lookup_dict_any_iterable(self):
|
def test_url_params_from_lookup_dict_any_iterable(self):
|
||||||
lookup1 = url_params_from_lookup_dict({'color__in': ('red', 'blue')})
|
lookup1 = widgets.url_params_from_lookup_dict({'color__in': ('red', 'blue')})
|
||||||
lookup2 = url_params_from_lookup_dict({'color__in': ['red', 'blue']})
|
lookup2 = widgets.url_params_from_lookup_dict({'color__in': ['red', 'blue']})
|
||||||
self.assertEqual(lookup1, {'color__in': 'red,blue'})
|
self.assertEqual(lookup1, {'color__in': 'red,blue'})
|
||||||
self.assertEqual(lookup1, lookup2)
|
self.assertEqual(lookup1, lookup2)
|
||||||
|
|
||||||
|
|
||||||
class FilteredSelectMultipleWidgetTest(DjangoTestCase):
|
class FilteredSelectMultipleWidgetTest(DjangoTestCase):
|
||||||
def test_render(self):
|
def test_render(self):
|
||||||
w = FilteredSelectMultiple('test', False)
|
w = widgets.FilteredSelectMultiple('test', False)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', 'test')),
|
conditional_escape(w.render('test', 'test')),
|
||||||
'<select multiple="multiple" name="test" class="selectfilter">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 0, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
|
'<select multiple="multiple" name="test" class="selectfilter">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 0, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_stacked_render(self):
|
def test_stacked_render(self):
|
||||||
w = FilteredSelectMultiple('test', True)
|
w = widgets.FilteredSelectMultiple('test', True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', 'test')),
|
conditional_escape(w.render('test', 'test')),
|
||||||
'<select multiple="multiple" name="test" class="selectfilterstacked">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 1, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
|
'<select multiple="multiple" name="test" class="selectfilterstacked">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 1, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
|
||||||
|
@ -210,14 +207,14 @@ class FilteredSelectMultipleWidgetTest(DjangoTestCase):
|
||||||
|
|
||||||
class AdminSplitDateTimeWidgetTest(DjangoTestCase):
|
class AdminSplitDateTimeWidgetTest(DjangoTestCase):
|
||||||
def test_render(self):
|
def test_render(self):
|
||||||
w = AdminSplitDateTime()
|
w = widgets.AdminSplitDateTime()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))),
|
conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))),
|
||||||
'<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>',
|
'<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_localization(self):
|
def test_localization(self):
|
||||||
w = AdminSplitDateTime()
|
w = widgets.AdminSplitDateTime()
|
||||||
|
|
||||||
with self.settings(USE_L10N=True):
|
with self.settings(USE_L10N=True):
|
||||||
with translation.override('de-at'):
|
with translation.override('de-at'):
|
||||||
|
@ -235,7 +232,7 @@ class AdminFileWidgetTest(DjangoTestCase):
|
||||||
name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
|
name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
|
||||||
)
|
)
|
||||||
|
|
||||||
w = AdminFileWidget()
|
w = widgets.AdminFileWidget()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', album.cover_art)),
|
conditional_escape(w.render('test', album.cover_art)),
|
||||||
'<p class="file-upload">Currently: <a href="%(STORAGE_URL)salbums/hybrid_theory.jpg">albums\hybrid_theory.jpg</a> <span class="clearable-file-input"><input type="checkbox" name="test-clear" id="test-clear_id" /> <label for="test-clear_id">Clear</label></span><br />Change: <input type="file" name="test" /></p>' % { 'STORAGE_URL': default_storage.url('') },
|
'<p class="file-upload">Currently: <a href="%(STORAGE_URL)salbums/hybrid_theory.jpg">albums\hybrid_theory.jpg</a> <span class="clearable-file-input"><input type="checkbox" name="test-clear" id="test-clear_id" /> <label for="test-clear_id">Clear</label></span><br />Change: <input type="file" name="test" /></p>' % { 'STORAGE_URL': default_storage.url('') },
|
||||||
|
@ -255,10 +252,10 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
)
|
)
|
||||||
rel = models.Album._meta.get_field('band').rel
|
rel = models.Album._meta.get_field('band').rel
|
||||||
|
|
||||||
w = ForeignKeyRawIdWidget(rel)
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', band.pk, attrs={})),
|
conditional_escape(w.render('test', band.pk, attrs={})),
|
||||||
'<input type="text" name="test" value="%(bandpk)s" 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/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Linkin Park</strong>' % dict(admin_media_prefix(), bandpk=band.pk),
|
'<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Linkin Park</strong>' % dict(admin_media_prefix(), bandpk=band.pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_relations_to_non_primary_key(self):
|
def test_relations_to_non_primary_key(self):
|
||||||
|
@ -270,17 +267,42 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
barcode=87, name='Core', parent=apple
|
barcode=87, name='Core', parent=apple
|
||||||
)
|
)
|
||||||
rel = models.Inventory._meta.get_field('parent').rel
|
rel = models.Inventory._meta.get_field('parent').rel
|
||||||
w = ForeignKeyRawIdWidget(rel)
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
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/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Apple</strong>' % admin_media_prefix(),
|
'<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Apple</strong>' % admin_media_prefix()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_fk_related_model_not_in_admin(self):
|
||||||
|
# FK to a model not registered with admin site. Raw ID widget shoud
|
||||||
|
# have no magnifying glass link. See #16542
|
||||||
|
big_honeycomb = models.Honeycomb.objects.create(location='Old tree')
|
||||||
|
big_honeycomb.bee_set.create()
|
||||||
|
rel = models.Bee._meta.get_field('honeycomb').rel
|
||||||
|
|
||||||
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
|
self.assertEqual(
|
||||||
|
conditional_escape(w.render('honeycomb_widget', big_honeycomb.pk, attrs={})),
|
||||||
|
'<input type="text" name="honeycomb_widget" value="%(hcombpk)s" /> <strong>Honeycomb object</strong>' % {'hcombpk': big_honeycomb.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_fk_to_self_model_not_in_admin(self):
|
||||||
|
# FK to self, not registered with admin site. Raw ID widget shoud have
|
||||||
|
# no magnifying glass link. See #16542
|
||||||
|
subject1 = models.Individual.objects.create(name='Subject #1')
|
||||||
|
models.Individual.objects.create(name='Child', parent=subject1)
|
||||||
|
rel = models.Individual._meta.get_field('parent').rel
|
||||||
|
|
||||||
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
|
self.assertEqual(
|
||||||
|
conditional_escape(w.render('individual_widget', subject1.pk, attrs={})),
|
||||||
|
'<input type="text" name="individual_widget" value="%(subj1pk)s" /> <strong>Individual object</strong>' % {'subj1pk': subject1.pk}
|
||||||
|
)
|
||||||
|
|
||||||
def test_proper_manager_for_label_lookup(self):
|
def test_proper_manager_for_label_lookup(self):
|
||||||
# see #9258
|
# see #9258
|
||||||
rel = models.Inventory._meta.get_field('parent').rel
|
rel = models.Inventory._meta.get_field('parent').rel
|
||||||
w = ForeignKeyRawIdWidget(rel)
|
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
|
||||||
|
|
||||||
hidden = models.Inventory.objects.create(
|
hidden = models.Inventory.objects.create(
|
||||||
barcode=93, name='Hidden', hidden=True
|
barcode=93, name='Hidden', hidden=True
|
||||||
|
@ -290,31 +312,28 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
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/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Hidden</strong>' % admin_media_prefix(),
|
'<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Hidden</strong>' % admin_media_prefix()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManyToManyRawIdWidgetTest(DjangoTestCase):
|
class ManyToManyRawIdWidgetTest(DjangoTestCase):
|
||||||
def test_render(self):
|
def test_render(self):
|
||||||
band = models.Band.objects.create(name='Linkin Park')
|
band = models.Band.objects.create(name='Linkin Park')
|
||||||
band.album_set.create(
|
|
||||||
name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
|
|
||||||
)
|
|
||||||
|
|
||||||
m1 = models.Member.objects.create(name='Chester')
|
m1 = models.Member.objects.create(name='Chester')
|
||||||
m2 = models.Member.objects.create(name='Mike')
|
m2 = models.Member.objects.create(name='Mike')
|
||||||
band.members.add(m1, m2)
|
band.members.add(m1, m2)
|
||||||
rel = models.Band._meta.get_field('members').rel
|
rel = models.Band._meta.get_field('members').rel
|
||||||
|
|
||||||
w = ManyToManyRawIdWidget(rel)
|
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})),
|
conditional_escape(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="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk),
|
'<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/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_media_prefix(), m1pk=m1.pk, m2pk=m2.pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
conditional_escape(w.render('test', [m1.pk])),
|
conditional_escape(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_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk),
|
'<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_media_prefix(), m1pk=m1.pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(w._has_changed(None, None), False)
|
self.assertEqual(w._has_changed(None, None), False)
|
||||||
|
@ -324,10 +343,31 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
|
||||||
self.assertEqual(w._has_changed([1, 2], [u'1']), True)
|
self.assertEqual(w._has_changed([1, 2], [u'1']), True)
|
||||||
self.assertEqual(w._has_changed([1, 2], [u'1', u'3']), True)
|
self.assertEqual(w._has_changed([1, 2], [u'1', u'3']), True)
|
||||||
|
|
||||||
|
def test_m2m_related_model_not_in_admin(self):
|
||||||
|
# M2M relationship with model not registered with admin site. Raw ID
|
||||||
|
# widget shoud have no magnifying glass link. See #16542
|
||||||
|
consultor1 = models.Advisor.objects.create(name='Rockstar Techie')
|
||||||
|
|
||||||
|
c1 = models.Company.objects.create(name='Doodle')
|
||||||
|
c2 = models.Company.objects.create(name='Pear')
|
||||||
|
consultor1.companies.add(c1, c2)
|
||||||
|
rel = models.Advisor._meta.get_field('companies').rel
|
||||||
|
|
||||||
|
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
|
||||||
|
self.assertEqual(
|
||||||
|
conditional_escape(w.render('company_widget1', [c1.pk, c2.pk], attrs={})),
|
||||||
|
'<input type="text" name="company_widget1" value="%(c1pk)s,%(c2pk)s" />' % {'c1pk': c1.pk, 'c2pk': c2.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
conditional_escape(w.render('company_widget2', [c1.pk])),
|
||||||
|
'<input type="text" name="company_widget2" value="%(c1pk)s" />' % {'c1pk': c1.pk}
|
||||||
|
)
|
||||||
|
|
||||||
class RelatedFieldWidgetWrapperTests(DjangoTestCase):
|
class RelatedFieldWidgetWrapperTests(DjangoTestCase):
|
||||||
def test_no_can_add_related(self):
|
def test_no_can_add_related(self):
|
||||||
rel = models.Inventory._meta.get_field('parent').rel
|
rel = models.Individual._meta.get_field('parent').rel
|
||||||
w = AdminRadioSelect()
|
w = widgets.AdminRadioSelect()
|
||||||
# Used to fail with a name error.
|
# Used to fail with a name error.
|
||||||
w = RelatedFieldWidgetWrapper(w, rel, admin.site)
|
w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
|
||||||
self.assertFalse(w.can_add_related)
|
self.assertFalse(w.can_add_related)
|
||||||
|
|
|
@ -27,4 +27,14 @@ site = WidgetAdmin(name='widget-admin')
|
||||||
site.register(models.User)
|
site.register(models.User)
|
||||||
site.register(models.Car, CarAdmin)
|
site.register(models.Car, CarAdmin)
|
||||||
site.register(models.CarTire, CarTireAdmin)
|
site.register(models.CarTire, CarTireAdmin)
|
||||||
|
|
||||||
|
site.register(models.Member)
|
||||||
|
site.register(models.Band)
|
||||||
site.register(models.Event, EventAdmin)
|
site.register(models.Event, EventAdmin)
|
||||||
|
site.register(models.Album)
|
||||||
|
|
||||||
|
site.register(models.Inventory)
|
||||||
|
|
||||||
|
site.register(models.Bee)
|
||||||
|
|
||||||
|
site.register(models.Advisor)
|
||||||
|
|
Loading…
Reference in New Issue