Ticket #17198: 17198.changelist-order.diff
File 17198.changelist-order.diff, 10.8 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/views/main.py
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 32113f5..e5f0543 100644
a b class ChangeList(object): 220 220 221 221 def get_ordering(self, request): 222 222 params = self.params 223 # For ordering, first check the if exists the "get_ordering" method 224 # in model admin, then check "ordering" parameter in the admin 225 # options, then check the object's default ordering. Finally, a 223 # For ordering, first check the "get_ordering" method in model admin, 224 # then check the object's default ordering. Finally, a 226 225 # manually-specified ordering from the query string overrides anything. 227 226 ordering = self.model_admin.get_ordering(request) or self._get_default_ordering() 228 227 if ORDER_VAR in params: … … class ChangeList(object): 239 238 ordering.append(pfx + order_field) 240 239 except (IndexError, ValueError): 241 240 continue # Invalid ordering specified, skip it. 241 # Ensure that the primary key is systematically present in the list of 242 # ordering fields so we can guarantee a deterministic order. 243 pk_name = self.lookup_opts.pk.name 244 if not (set(ordering) & set(['pk', '-pk', pk_name, '-' + pk_name])): 245 # The two sets do not intersect, meaning the pk isn't present. So 246 # we add it. 247 ordering = list(ordering) + ['pk'] 242 248 return ordering 243 249 244 250 def get_ordering_field_columns(self): -
tests/regressiontests/admin_changelist/models.py
diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py index 97080fb..efa392a 100644
a b class Swallow(models.Model): 57 57 58 58 class Meta: 59 59 ordering = ('speed', 'load') 60 61 62 class UnorderedObject(models.Model): 63 """ 64 Model without any defined `Meta.ordering`. 65 Refs #17198. 66 """ 67 name = models.CharField(max_length=255) 68 bool = models.BooleanField(default=True) 69 No newline at end of file -
tests/regressiontests/admin_changelist/tests.py
diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py index b422519..84cad61 100644
a b from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin, 14 14 FilteredChildAdmin, CustomPaginator, site as custom_site, 15 15 SwallowAdmin) 16 16 from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet, 17 Membership, ChordsMusician, ChordsBand, Invitation, Swallow) 17 Membership, ChordsMusician, ChordsBand, Invitation, Swallow, 18 UnorderedObject) 18 19 19 20 20 21 class ChangeListTests(TestCase): … … class ChangeListTests(TestCase): 430 431 self.assertContains(response, unicode(swallow.load)) 431 432 self.assertContains(response, unicode(swallow.speed)) 432 433 434 def test_default_ordering_by_pk(self): 435 """ 436 Ensure that the primary key is systematically used in the ordering of 437 the changelist's results to guarantee a deterministic order. 438 Refs #17198. 439 """ 440 superuser = self._create_superuser('superuser') 441 for counter in range(1, 51): 442 UnorderedObject.objects.create( 443 id=counter, name='Unordered object #%s' % counter, bool=True) 444 445 class UnorderedObjectAdmin(admin.ModelAdmin): 446 list_per_page = 10 447 list_display = ['name'] 448 449 def check_results_order(reverse=False): 450 admin.site.register(UnorderedObject, UnorderedObjectAdmin) 451 model_admin = UnorderedObjectAdmin(UnorderedObject, admin.site) 452 counter = 51 if reverse else 0 453 for page in range (0, 5): 454 request = self._mocked_authenticated_request('/unorderedobject/?p=%s' % page, superuser) 455 response = model_admin.changelist_view(request) 456 for result in response.context_data['cl'].result_list: 457 counter += -1 if reverse else 1 458 self.assertEqual(result.id, counter) 459 admin.site.unregister(UnorderedObject) 460 461 # When no order is defined at all 462 check_results_order() 463 464 # When an order is defined 465 UnorderedObjectAdmin.ordering = ['bool'] 466 check_results_order() 467 468 # When an order is defined, including the pk itself 469 UnorderedObjectAdmin.ordering = ['bool', '-pk'] 470 check_results_order(reverse=True) 471 UnorderedObjectAdmin.ordering = ['bool', 'pk'] 472 check_results_order() 473 UnorderedObjectAdmin.ordering = ['-id', 'bool'] 474 check_results_order(reverse=True) 475 UnorderedObjectAdmin.ordering = ['id', 'bool'] 476 check_results_order() 477 No newline at end of file -
tests/regressiontests/admin_views/admin.py
diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py index b10d178..d960749 100644
a b from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, 26 26 CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, 27 27 Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, 28 28 AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, 29 AdminOrderedCallable, Report, Color2, MainPrepopulated, RelatedPrepopulated) 29 AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, 30 RelatedPrepopulated) 30 31 31 32 32 33 def callable_year(dt_value): … … class MainPrepopulatedAdmin(admin.ModelAdmin): 562 563 'slug2': ['status', 'name']} 563 564 564 565 566 class UnorderedObjectAdmin(admin.ModelAdmin): 567 list_display = ['name'] 568 list_editable = ['name'] 569 list_per_page = 2 570 565 571 566 572 567 573 site = admin.AdminSite(name="admin") … … site.register(Story, StoryAdmin) 609 615 site.register(OtherStory, OtherStoryAdmin) 610 616 site.register(Report, ReportAdmin) 611 617 site.register(MainPrepopulated, MainPrepopulatedAdmin) 618 site.register(UnorderedObject, UnorderedObjectAdmin) 612 619 613 620 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. 614 621 # That way we cover all four cases: -
tests/regressiontests/admin_views/models.py
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index cf2896f..c8cd0e8 100644
a b class RelatedPrepopulated(models.Model): 596 596 choices=(('option one', 'Option One'), 597 597 ('option two', 'Option Two'))) 598 598 slug1 = models.SlugField(max_length=50) 599 slug2 = models.SlugField(max_length=60) 600 No newline at end of file 599 slug2 = models.SlugField(max_length=60) 600 601 602 class UnorderedObject(models.Model): 603 """ 604 Model without any defined `Meta.ordering`. 605 Refs #16819. 606 """ 607 name = models.CharField(max_length=255) 608 bool = models.BooleanField(default=True) 609 No newline at end of file -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 760f31a..a6d79d8 100755
a b from django.core.exceptions import SuspiciousOperation 12 12 from django.core.files import temp as tempfile 13 13 from django.core.urlresolvers import reverse 14 14 # Register auth models with the admin. 15 from django.contrib import admin 15 16 from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME 16 17 from django.contrib.admin.models import LogEntry, DELETION 17 18 from django.contrib.admin.sites import LOGIN_FORM_KEY … … from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount, 41 42 FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story, 42 43 OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField, 43 44 AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, 44 Report, MainPrepopulated, RelatedPrepopulated )45 Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject) 45 46 46 47 47 48 ERROR_MESSAGE = "Please enter the correct username and password \ … … class AdminViewBasicTest(TestCase): 272 273 ) 273 274 274 275 def testChangeListSortingPreserveQuerySetOrdering(self): 275 # If no ordering on ModelAdmin, or query string, the underlying order of276 # the queryset should not be changed.276 # If no ordering on ModelAdmin, or query string, the queryset should be 277 # ordered by ascending pk. 277 278 278 279 p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80) 279 280 p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70) … … class AdminViewBasicTest(TestCase): 285 286 response = self.client.get('/test_admin/admin/admin_views/person/', {}) 286 287 self.assertEqual(response.status_code, 200) 287 288 self.assertTrue( 288 response.content.index(link % p 3.id) < response.content.index(link % p2.id) and289 response.content.index(link % p2.id) < response.content.index(link % p 1.id)289 response.content.index(link % p1.id) < response.content.index(link % p2.id) and 290 response.content.index(link % p2.id) < response.content.index(link % p3.id) 290 291 ) 291 292 292 293 def testChangeListSortingModelMeta(self): … … class AdminViewListEditable(TestCase): 1873 1874 self.assertEqual(Category.objects.get(id=3).order, 1) 1874 1875 self.assertEqual(Category.objects.get(id=4).order, 0) 1875 1876 1877 def test_list_editable_ordering_default(self): 1878 """ 1879 Ensure that the list_editable items on the changelist are ordered 1880 in a deterministic order and without raising exceptions, even when 1881 no default ordering is explicitly defined. 1882 Refs #16819. 1883 """ 1884 UnorderedObject.objects.create(id=1, name='Unordered object #1') 1885 UnorderedObject.objects.create(id=2, name='Unordered object #2') 1886 UnorderedObject.objects.create(id=3, name='Unordered object #3') 1887 response = self.client.get('/test_admin/admin/admin_views/unorderedobject/') 1888 self.assertContains(response, 'Unordered object #1') 1889 self.assertContains(response, 'Unordered object #2') 1890 self.assertNotContains(response, 'Unordered object #3') 1891 response = self.client.get('/test_admin/admin/admin_views/unorderedobject/?p=1') 1892 self.assertNotContains(response, 'Unordered object #1') 1893 self.assertNotContains(response, 'Unordered object #2') 1894 self.assertContains(response, 'Unordered object #3') 1895 1876 1896 def test_list_editable_action_submit(self): 1877 1897 # List editable changes should not be executed if the action "Go" button is 1878 1898 # used to submit the form.