Ticket #16542: 16542.2.diff
File 16542.2.diff, 20.5 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
a b 154 154 """ 155 155 db = kwargs.get('using') 156 156 if db_field.name in self.raw_id_fields: 157 kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, using=db) 157 kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, 158 self.admin_site, using=db) 158 159 elif db_field.name in self.radio_fields: 159 160 kwargs['widget'] = widgets.AdminRadioSelect(attrs={ 160 161 'class': get_ul_class(self.radio_fields[db_field.name]), … … 174 175 db = kwargs.get('using') 175 176 176 177 if db_field.name in self.raw_id_fields: 177 kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db) 178 kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, 179 self.admin_site, using=db) 178 180 kwargs['help_text'] = '' 179 181 elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): 180 182 kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) -
django/contrib/admin/widgets.py
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
a b 112 112 A Widget for displaying ForeignKeys in the "raw_id" interface rather than 113 113 in a <select> box. 114 114 """ 115 def __init__(self, rel, a ttrs=None, using=None):115 def __init__(self, rel, admin_site, attrs=None, using=None): 116 116 self.rel = rel 117 self.admin_site = admin_site 117 118 self.db = using 118 119 super(ForeignKeyRawIdWidget, self).__init__(attrs) 119 120 120 121 def render(self, name, value, attrs=None): 122 rel_to = self.rel.to 121 123 if attrs is None: 122 124 attrs = {} 123 related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) 124 params = self.url_parameters() 125 if params: 126 url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()]) 127 else: 128 url = u'' 129 if "class" not in attrs: 130 attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook. 131 output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] 132 # TODO: "id_" is hard-coded here. This should instead use the correct 133 # API to determine the ID dynamically. 134 output.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' 135 % (related_url, url, name)) 136 output.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>' 137 % (static('admin/img/selector-search.gif'), _('Lookup'))) 125 extra = [] 126 if rel_to in self.admin_site._registry: # If the related object has an admin interface: 127 try: 128 related_url = reverse('admin:%s_%s_changelist' % 129 (rel_to._meta.app_label, 130 rel_to._meta.module_name), 131 current_app=self.admin_site.name) 132 except NoReverseMatch: 133 raise 134 135 params = self.url_parameters() 136 if params: 137 url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()]) 138 else: 139 url = u'' 140 if "class" not in attrs: 141 attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook. 142 # TODO: "lookup_id_" is hard-coded here. This should instead use 143 # the correct API to determine the ID dynamically. 144 extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' 145 % (related_url, url, name)) 146 extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>' 147 % (static('admin/img/selector-search.gif'), _('Lookup'))) 148 output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra 138 149 if value: 139 150 output.append(self.label_for_value(value)) 140 151 return mark_safe(u''.join(output)) … … 164 175 def render(self, name, value, attrs=None): 165 176 if attrs is None: 166 177 attrs = {} 167 attrs['class'] = 'vManyToManyRawIdAdminField' 178 if self.rel.to in self.admin_site._registry: # If the related object has an admin interface: 179 attrs['class'] = 'vManyToManyRawIdAdminField' 168 180 if value: 169 181 value = ','.join([force_unicode(v) for v in value]) 170 182 else: … … 232 244 output = [self.widget.render(name, value, *args, **kwargs)] 233 245 if self.can_add_related: 234 246 related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) 235 # TODO: " id_" is hard-coded here. This should instead use the correct236 # API to determine the ID dynamically.247 # TODO: "add_id_" is hard-coded here. This should instead use the 248 # correct API to determine the ID dynamically. 237 249 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' 238 250 % (related_url, name)) 239 251 output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>' -
tests/regressiontests/admin_widgets/models.py
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
a b 67 67 A single car tire. This to test that a user can only select their own cars. 68 68 """ 69 69 car = models.ForeignKey(Car) 70 71 class Honeycomb(models.Model): 72 location = models.CharField(max_length=20) 73 74 class Bee(models.Model): 75 """ 76 A model with a FK to a model that won't be registered with the admin 77 (Honeycomb) so the corresponding raw ID widget won't have a magnifying 78 glass link to select related honeycomb instances. 79 """ 80 honeycomb = models.ForeignKey(Honeycomb) 81 82 class Individual(models.Model): 83 """ 84 A model with a FK to itself. It won't be registered with the admin, so the 85 corresponding raw ID widget won't have a magnifying glass link to select 86 related instances (rendering will be called programmatically in this case). 87 """ 88 name = models.CharField(max_length=20) 89 parent = models.ForeignKey('self', null=True) 90 91 class Company(models.Model): 92 name = models.CharField(max_length=20) 93 94 class Advisor(models.Model): 95 """ 96 A model with a m2m to a model that won't be registered with the admin 97 (Company) so the corresponding raw ID widget won't have a magnifying 98 glass link to select related company instances. 99 """ 100 name = models.CharField(max_length=20) 101 companies = models.ManyToManyField(Company) -
tests/regressiontests/admin_widgets/tests.py
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
a b 7 7 from django.conf import settings 8 8 from django.contrib import admin 9 9 from django.contrib.admin import widgets 10 from django.contrib.admin.widgets import (FilteredSelectMultiple,11 AdminSplitDateTime, AdminFileWidget, ForeignKeyRawIdWidget, AdminRadioSelect,12 RelatedFieldWidgetWrapper, ManyToManyRawIdWidget,13 url_params_from_lookup_dict)14 10 from django.core.files.storage import default_storage 15 11 from django.core.files.uploadedfile import SimpleUploadedFile 16 12 from django.db.models import DateField … … 20 16 from django.utils.unittest import TestCase 21 17 22 18 import models 19 from widgetadmin import site as widget_admin_site 23 20 24 21 admin_media_prefix = lambda: { 25 22 'ADMIN_MEDIA_PREFIX': "%sadmin/" % settings.STATIC_URL, … … 186 183 'Select a valid choice. That choice is not one of the available choices.') 187 184 188 185 def test_url_params_from_lookup_dict_any_iterable(self): 189 lookup1 = url_params_from_lookup_dict({'color__in': ('red', 'blue')})190 lookup2 = url_params_from_lookup_dict({'color__in': ['red', 'blue']})186 lookup1 = widgets.url_params_from_lookup_dict({'color__in': ('red', 'blue')}) 187 lookup2 = widgets.url_params_from_lookup_dict({'color__in': ['red', 'blue']}) 191 188 self.assertEqual(lookup1, {'color__in': 'red,blue'}) 192 189 self.assertEqual(lookup1, lookup2) 193 190 194 191 195 192 class FilteredSelectMultipleWidgetTest(DjangoTestCase): 196 193 def test_render(self): 197 w = FilteredSelectMultiple('test', False)194 w = widgets.FilteredSelectMultiple('test', False) 198 195 self.assertEqual( 199 196 conditional_escape(w.render('test', 'test')), 200 197 '<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() 201 198 ) 202 199 203 200 def test_stacked_render(self): 204 w = FilteredSelectMultiple('test', True)201 w = widgets.FilteredSelectMultiple('test', True) 205 202 self.assertEqual( 206 203 conditional_escape(w.render('test', 'test')), 207 204 '<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 207 211 208 class AdminSplitDateTimeWidgetTest(DjangoTestCase): 212 209 def test_render(self): 213 w = AdminSplitDateTime()210 w = widgets.AdminSplitDateTime() 214 211 self.assertEqual( 215 212 conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), 216 213 '<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>', 217 214 ) 218 215 219 216 def test_localization(self): 220 w = AdminSplitDateTime()217 w = widgets.AdminSplitDateTime() 221 218 222 219 with self.settings(USE_L10N=True): 223 220 with translation.override('de-at'): … … 235 232 name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg' 236 233 ) 237 234 238 w = AdminFileWidget()235 w = widgets.AdminFileWidget() 239 236 self.assertEqual( 240 237 conditional_escape(w.render('test', album.cover_art)), 241 238 '<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 252 ) 256 253 rel = models.Album._meta.get_field('band').rel 257 254 258 w = ForeignKeyRawIdWidget(rel)255 w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) 259 256 self.assertEqual( 260 257 conditional_escape(w.render('test', band.pk, attrs={})), 261 '<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),258 '<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) 262 259 ) 263 260 264 261 def test_relations_to_non_primary_key(self): … … 270 267 barcode=87, name='Core', parent=apple 271 268 ) 272 269 rel = models.Inventory._meta.get_field('parent').rel 273 w = ForeignKeyRawIdWidget(rel)270 w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) 274 271 self.assertEqual( 275 272 w.render('test', core.parent_id, attrs={}), 276 '<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(),273 '<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() 277 274 ) 278 275 276 def test_fk_related_model_not_in_admin(self): 277 # FK to a model not registered with admin site. Raw ID widget shoud 278 # have no magnifying glass link. 279 big_honeycomb = models.Honeycomb.objects.create(location='Old tree') 280 big_honeycomb.bee_set.create() 281 rel = models.Bee._meta.get_field('honeycomb').rel 282 283 w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) 284 self.assertEqual( 285 conditional_escape(w.render('honeycomb_widget', big_honeycomb.pk, attrs={})), 286 '<input type="text" name="honeycomb_widget" value="%(hcombpk)s" /> <strong>Honeycomb object</strong>' % {'hcombpk': big_honeycomb.pk} 287 ) 288 289 def test_fk_to_self_model_not_in_admin(self): 290 # FK to self, not registered with admin site. Raw ID widget shoud have 291 # no magnifying glass link. 292 subject1 = models.Individual.objects.create(name='Subject #1') 293 models.Individual.objects.create(name='Child', parent=subject1) 294 rel = models.Individual._meta.get_field('parent').rel 295 296 w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) 297 self.assertEqual( 298 conditional_escape(w.render('individual_widget', subject1.pk, attrs={})), 299 '<input type="text" name="individual_widget" value="%(subj1pk)s" /> <strong>Individual object</strong>' % {'subj1pk': subject1.pk} 300 ) 279 301 280 302 def test_proper_manager_for_label_lookup(self): 281 303 # see #9258 282 304 rel = models.Inventory._meta.get_field('parent').rel 283 w = ForeignKeyRawIdWidget(rel)305 w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) 284 306 285 307 hidden = models.Inventory.objects.create( 286 308 barcode=93, name='Hidden', hidden=True … … 290 312 ) 291 313 self.assertEqual( 292 314 w.render('test', child_of_hidden.parent_id, attrs={}), 293 '<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(),315 '<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() 294 316 ) 295 317 296 318 297 319 class ManyToManyRawIdWidgetTest(DjangoTestCase): 298 320 def test_render(self): 299 321 band = models.Band.objects.create(name='Linkin Park') 300 band.album_set.create(301 name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'302 )303 322 304 323 m1 = models.Member.objects.create(name='Chester') 305 324 m2 = models.Member.objects.create(name='Mike') 306 325 band.members.add(m1, m2) 307 326 rel = models.Band._meta.get_field('members').rel 308 327 309 w = ManyToManyRawIdWidget(rel)328 w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) 310 329 self.assertEqual( 311 330 conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})), 312 '<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),331 '<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) 313 332 ) 314 333 315 334 self.assertEqual( 316 335 conditional_escape(w.render('test', [m1.pk])), 317 '<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),336 '<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) 318 337 ) 319 338 320 339 self.assertEqual(w._has_changed(None, None), False) … … 324 343 self.assertEqual(w._has_changed([1, 2], [u'1']), True) 325 344 self.assertEqual(w._has_changed([1, 2], [u'1', u'3']), True) 326 345 346 def test_m2m_related_model_not_in_admin(self): 347 # M2M relatinship with model not registered with admin site. Raw ID 348 # widget shoud have no magnifying glass link. 349 consultor1 = models.Advisor.objects.create(name='Rockstar Techie') 350 351 c1 = models.Company.objects.create(name='Doodle') 352 c2 = models.Company.objects.create(name='Pear') 353 consultor1.companies.add(c1, c2) 354 rel = models.Advisor._meta.get_field('companies').rel 355 356 w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) 357 self.assertEqual( 358 conditional_escape(w.render('company_widget1', [c1.pk, c2.pk], attrs={})), 359 '<input type="text" name="company_widget1" value="%(c1pk)s,%(c2pk)s" />' % {'c1pk': c1.pk, 'c2pk': c2.pk} 360 ) 361 362 self.assertEqual( 363 conditional_escape(w.render('company_widget2', [c1.pk])), 364 '<input type="text" name="company_widget2" value="%(c1pk)s" />' % {'c1pk': c1.pk} 365 ) 366 327 367 class RelatedFieldWidgetWrapperTests(DjangoTestCase): 328 368 def test_no_can_add_related(self): 329 rel = models.In ventory._meta.get_field('parent').rel330 w = AdminRadioSelect()369 rel = models.Individual._meta.get_field('parent').rel 370 w = widgets.AdminRadioSelect() 331 371 # Used to fail with a name error. 332 w = RelatedFieldWidgetWrapper(w, rel, admin.site)372 w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) 333 373 self.assertFalse(w.can_add_related) -
tests/regressiontests/admin_widgets/widgetadmin.py
diff --git a/tests/regressiontests/admin_widgets/widgetadmin.py b/tests/regressiontests/admin_widgets/widgetadmin.py
a b 27 27 site.register(models.User) 28 28 site.register(models.Car, CarAdmin) 29 29 site.register(models.CarTire, CarTireAdmin) 30 31 site.register(models.Member) 32 site.register(models.Band) 30 33 site.register(models.Event, EventAdmin) 34 site.register(models.Album) 35 36 site.register(models.Inventory) 37 38 site.register(models.Bee) 39 40 site.register(models.Advisor)