Ticket #17252: r17130-admin-order-field-better-test.diff
File r17130-admin-order-field-better-test.diff, 9.9 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/views/main.py
169 169 ordering = self.lookup_opts.ordering 170 170 return ordering 171 171 172 def _get_admin_order_field(self, field_name): 173 """ 174 Get the name of the admin order field defined for the given 175 element of 'list_display'. The given object can either be the 176 name of a proper field, the name of a method (on the admin or 177 model) with the 'admin_order_field' attribute, or a callable 178 with that attribute. If no admin order is defined for the 179 given object, the method returns None. 180 """ 181 try: 182 f = self.lookup_opts.get_field(field_name) 183 except models.FieldDoesNotExist: 184 # See whether field_name is a name of a non-field 185 # that allows sorting. 186 try: 187 if callable(field_name): 188 attr = field_name 189 elif hasattr(self.model_admin, field_name): 190 attr = getattr(self.model_admin, field_name) 191 else: 192 attr = getattr(self.model, field_name) 193 return attr.admin_order_field 194 except AttributeError: 195 return None 196 else: 197 return f.name 198 172 199 def get_ordering(self, request): 173 200 params = self.params 174 201 # For ordering, first check the if exists the "get_ordering" method … … 185 212 try: 186 213 none, pfx, idx = p.rpartition('-') 187 214 field_name = self.list_display[int(idx)] 188 try: 189 f = self.lookup_opts.get_field(field_name) 190 except models.FieldDoesNotExist: 191 # See whether field_name is a name of a non-field 192 # that allows sorting. 193 try: 194 if callable(field_name): 195 attr = field_name 196 elif hasattr(self.model_admin, field_name): 197 attr = getattr(self.model_admin, field_name) 198 else: 199 attr = getattr(self.model, field_name) 200 field_name = attr.admin_order_field 201 except AttributeError: 202 continue # No 'admin_order_field', skip it 203 else: 204 field_name = f.name 215 order_field = self._get_admin_order_field(field_name) 216 if not order_field: 217 continue # No 'admin_order_field', skip it 205 218 206 ordering.append(pfx + field_name)219 ordering.append(pfx + order_field) 207 220 208 221 except (IndexError, ValueError): 209 222 continue # Invalid ordering specified, skip it. … … 235 248 # No match, but there might be a match if we take into 236 249 # account 'admin_order_field' 237 250 for _index, attr in enumerate(self.list_display): 238 if getattr(attr, 'admin_order_field', '') == field:251 if self._get_admin_order_field(attr) == field: 239 252 index = _index 240 253 break 241 254 if index is not None: -
tests/regressiontests/admin_views/admin.py
22 22 Gadget, Villain, SuperVillain, Plot, PlotDetails, CyclicOne, CyclicTwo, 23 23 WorkHour, Reservation, FoodDelivery, RowLevelChangePermissionModel, Paper, 24 24 CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, 25 Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug) 25 Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, 26 AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, 27 AdminOrderedCallable) 26 28 27 29 28 30 def callable_year(dt_value): … … 474 476 'slug' : ('title',) 475 477 } 476 478 479 480 class AdminOrderedFieldAdmin(admin.ModelAdmin): 481 ordering = ('order',) 482 list_display = ('stuff', 'order') 483 484 class AdminOrderedModelMethodAdmin(admin.ModelAdmin): 485 ordering = ('order',) 486 list_display = ('stuff', 'some_order') 487 488 class AdminOrderedAdminMethodAdmin(admin.ModelAdmin): 489 def some_admin_order(self, obj): 490 return obj.order 491 some_admin_order.admin_order_field = 'order' 492 ordering = ('order',) 493 list_display = ('stuff', 'some_admin_order') 494 495 def admin_ordered_callable(obj): 496 return obj.order 497 admin_ordered_callable.admin_order_field = 'order' 498 class AdminOrderedCallableAdmin(admin.ModelAdmin): 499 ordering = ('order',) 500 list_display = ('stuff', admin_ordered_callable) 501 502 477 503 site = admin.AdminSite(name="admin") 478 504 site.register(Article, ArticleAdmin) 479 505 site.register(CustomArticle, CustomArticleAdmin) … … 537 563 site.register(Answer) 538 564 site.register(PrePopulatedPost, PrePopulatedPostAdmin) 539 565 site.register(ComplexSortedPerson, ComplexSortedPersonAdmin) 566 site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin) 567 site.register(AdminOrderedField, AdminOrderedFieldAdmin) 568 site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin) 569 site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin) 570 site.register(AdminOrderedCallable, AdminOrderedCallableAdmin) 540 571 541 572 # Register core models we need in our tests 542 573 from django.contrib.auth.models import User, Group 543 574 from django.contrib.auth.admin import UserAdmin, GroupAdmin 544 575 site.register(User, UserAdmin) 545 576 site.register(Group, GroupAdmin) 546 site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin) -
tests/regressiontests/admin_views/tests.py
37 37 DooHickey, FancyDoodad, Whatsit, Category, Post, Plot, FunkyTag, Chapter, 38 38 Book, Promo, WorkHour, Employee, Question, Answer, Inquisition, Actor, 39 39 FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story, 40 OtherStory, ComplexSortedPerson, Parent, Child) 40 OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField, 41 AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable) 41 42 42 43 43 44 ERROR_MESSAGE = "Please enter the correct username and password \ … … 354 355 response.content.index(link % p2.id) < response.content.index(link % p1.id) 355 356 ) 356 357 358 def testSortIndicatorsAdminOrder(self): 359 """ 360 Ensures that the admin shows default sort indicators for all 361 kinds of 'ordering' fields: field names, method on the model 362 admin and model itself, and other callables. See #17252. 363 """ 364 models = [(AdminOrderedField, 'adminorderedfield' ), 365 (AdminOrderedModelMethod, 'adminorderedmodelmethod'), 366 (AdminOrderedAdminMethod, 'adminorderedadminmethod'), 367 (AdminOrderedCallable, 'adminorderedcallable' )] 368 for model, url in models: 369 a1 = model.objects.create(stuff='The Last Item', order=3) 370 a2 = model.objects.create(stuff='The First Item', order=1) 371 a3 = model.objects.create(stuff='The Middle Item', order=2) 372 response = self.client.get('/test_admin/admin/admin_views/%s/' % url, {}) 373 self.assertEqual(response.status_code, 200) 374 # Should have 3 columns including action checkbox col. 375 self.assertContains(response, '<th scope="col"', count=3, msg_prefix=url) 376 # Check if the correct column was selected. 2 is the index of the 377 # 'order' column in the model admin's 'list_display' with 0 being 378 # the implicit 'action_checkbox' and 1 being the column 'stuff'. 379 self.assertEqual(response.context['cl'].get_ordering_field_columns(), {2: 'asc'}) 380 # Check order of records. 381 self.assertTrue(response.content.index('The First Item') < 382 response.content.index('The Middle Item') < response.content.index('The Last Item')) 383 357 384 def testLimitedFilter(self): 358 385 """Ensure admin changelist filters do not contain objects excluded via limit_choices_to. 359 386 This also tests relation-spanning filters (e.g. 'color__value'). -
tests/regressiontests/admin_views/models.py
538 538 age = models.PositiveIntegerField() 539 539 is_employee = models.NullBooleanField() 540 540 541 541 542 class PrePopulatedPostLargeSlug(models.Model): 542 543 """ 543 544 Regression test for #15938: a large max_length for the slugfield must not … … 548 549 published = models.BooleanField() 549 550 slug = models.SlugField(max_length=1000) 550 551 552 553 class AdminOrderedField(models.Model): 554 order = models.IntegerField() 555 stuff = models.CharField(max_length=200) 556 557 class AdminOrderedModelMethod(models.Model): 558 order = models.IntegerField() 559 stuff = models.CharField(max_length=200) 560 def some_order(self): 561 return self.order 562 some_order.admin_order_field = 'order' 563 564 class AdminOrderedAdminMethod(models.Model): 565 order = models.IntegerField() 566 stuff = models.CharField(max_length=200) 567 568 class AdminOrderedCallable(models.Model): 569 order = models.IntegerField() 570 stuff = models.CharField(max_length=200)