From 290d7d4d21692336aeb074917acbf9804275fbdd Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Mon, 1 Aug 2011 23:38:11 +0000 Subject: [PATCH] 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 --- django/contrib/admin/options.py | 6 +- django/contrib/admin/widgets.py | 51 ++++++---- tests/regressiontests/admin_widgets/models.py | 32 +++++++ tests/regressiontests/admin_widgets/tests.py | 92 +++++++++++++------ .../admin_widgets/widgetadmin.py | 10 ++ 5 files changed, 143 insertions(+), 48 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index b0eb113359..35c3cde0fc 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -154,7 +154,8 @@ class BaseModelAdmin(object): """ db = kwargs.get('using') 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: kwargs['widget'] = widgets.AdminRadioSelect(attrs={ 'class': get_ul_class(self.radio_fields[db_field.name]), @@ -174,7 +175,8 @@ class BaseModelAdmin(object): db = kwargs.get('using') 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'] = '' 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)) diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 713ce63831..038351e6da 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -4,7 +4,7 @@ Form Widget classes specific to the Django admin site. import copy 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.util import flatatt 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 in a \n\n' % admin_media_prefix() ) def test_stacked_render(self): - w = FilteredSelectMultiple('test', True) + w = widgets.FilteredSelectMultiple('test', True) self.assertEqual( conditional_escape(w.render('test', 'test')), '\n' % admin_media_prefix() @@ -210,14 +207,14 @@ class FilteredSelectMultipleWidgetTest(DjangoTestCase): class AdminSplitDateTimeWidgetTest(DjangoTestCase): def test_render(self): - w = AdminSplitDateTime() + w = widgets.AdminSplitDateTime() self.assertEqual( conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), '

Date:
Time:

', ) def test_localization(self): - w = AdminSplitDateTime() + w = widgets.AdminSplitDateTime() with self.settings(USE_L10N=True): with translation.override('de-at'): @@ -235,7 +232,7 @@ class AdminFileWidgetTest(DjangoTestCase): name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg' ) - w = AdminFileWidget() + w = widgets.AdminFileWidget() self.assertEqual( conditional_escape(w.render('test', album.cover_art)), '

Currently: albums\hybrid_theory.jpg
Change:

' % { 'STORAGE_URL': default_storage.url('') }, @@ -255,10 +252,10 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): ) rel = models.Album._meta.get_field('band').rel - w = ForeignKeyRawIdWidget(rel) + w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertEqual( conditional_escape(w.render('test', band.pk, attrs={})), - ' Lookup Linkin Park' % dict(admin_media_prefix(), bandpk=band.pk), + ' Lookup Linkin Park' % dict(admin_media_prefix(), bandpk=band.pk) ) def test_relations_to_non_primary_key(self): @@ -270,17 +267,42 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): barcode=87, name='Core', parent=apple ) rel = models.Inventory._meta.get_field('parent').rel - w = ForeignKeyRawIdWidget(rel) + w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertEqual( w.render('test', core.parent_id, attrs={}), - ' Lookup Apple' % admin_media_prefix(), + ' Lookup Apple' % 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={})), + ' Honeycomb object' % {'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={})), + ' Individual object' % {'subj1pk': subject1.pk} + ) def test_proper_manager_for_label_lookup(self): # see #9258 rel = models.Inventory._meta.get_field('parent').rel - w = ForeignKeyRawIdWidget(rel) + w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) hidden = models.Inventory.objects.create( barcode=93, name='Hidden', hidden=True @@ -290,31 +312,28 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): ) self.assertEqual( w.render('test', child_of_hidden.parent_id, attrs={}), - ' Lookup Hidden' % admin_media_prefix(), + ' Lookup Hidden' % admin_media_prefix() ) class ManyToManyRawIdWidgetTest(DjangoTestCase): def test_render(self): 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') m2 = models.Member.objects.create(name='Mike') band.members.add(m1, m2) rel = models.Band._meta.get_field('members').rel - w = ManyToManyRawIdWidget(rel) + w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) self.assertEqual( conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})), - ' Lookup' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk), + ' Lookup' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk) ) self.assertEqual( conditional_escape(w.render('test', [m1.pk])), - ' Lookup' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk), + ' Lookup' % dict(admin_media_prefix(), m1pk=m1.pk) ) 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', 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={})), + '' % {'c1pk': c1.pk, 'c2pk': c2.pk} + ) + + self.assertEqual( + conditional_escape(w.render('company_widget2', [c1.pk])), + '' % {'c1pk': c1.pk} + ) + class RelatedFieldWidgetWrapperTests(DjangoTestCase): def test_no_can_add_related(self): - rel = models.Inventory._meta.get_field('parent').rel - w = AdminRadioSelect() + rel = models.Individual._meta.get_field('parent').rel + w = widgets.AdminRadioSelect() # 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) diff --git a/tests/regressiontests/admin_widgets/widgetadmin.py b/tests/regressiontests/admin_widgets/widgetadmin.py index 6f15d9208b..ea738125fe 100644 --- a/tests/regressiontests/admin_widgets/widgetadmin.py +++ b/tests/regressiontests/admin_widgets/widgetadmin.py @@ -27,4 +27,14 @@ site = WidgetAdmin(name='widget-admin') site.register(models.User) site.register(models.Car, CarAdmin) 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.Inventory) + +site.register(models.Bee) + +site.register(models.Advisor)