Ticket #17198: 17198.changelist-order.3.diff
File 17198.changelist-order.3.diff, 14.0 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..acd5106 100644
a b class ChangeList(object): 66 66 self.list_editable = () 67 67 else: 68 68 self.list_editable = list_editable 69 self.ordering = self.get_ordering(request)70 69 self.query = request.GET.get(SEARCH_VAR, '') 71 70 self.query_set = self.get_query_set(request) 72 71 self.get_results(request) … … class ChangeList(object): 218 217 attr = getattr(self.model, field_name) 219 218 return getattr(attr, 'admin_order_field', None) 220 219 221 def get_ordering(self, request ):220 def get_ordering(self, request, queryset): 222 221 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 222 # For ordering, first check the "get_ordering" method in model admin, 223 # then check the object's default ordering. Finally, a 226 224 # manually-specified ordering from the query string overrides anything. 227 ordering = self.model_admin.get_ordering(request) or self._get_default_ordering() 225 ordering = list(self.model_admin.get_ordering(request) 226 or self._get_default_ordering()) 228 227 if ORDER_VAR in params: 229 228 # Clear ordering and used params 230 229 ordering = [] … … class ChangeList(object): 239 238 ordering.append(pfx + order_field) 240 239 except (IndexError, ValueError): 241 240 continue # Invalid ordering specified, skip it. 241 242 # Add the given query's ordering fields, if any. 243 ordering.extend(queryset.query.order_by) 244 245 # Ensure that the primary key is systematically present in the list of 246 # ordering fields so we can guarantee a deterministic order. 247 pk_name = self.lookup_opts.pk.name 248 if not (set(ordering) & set(['pk', '-pk', pk_name, '-' + pk_name])): 249 # The two sets do not intersect, meaning the pk isn't present. So 250 # we add it. 251 ordering.append('pk') 252 242 253 return ordering 243 254 244 255 def get_ordering_field_columns(self): … … class ChangeList(object): 322 333 break 323 334 324 335 # Set ordering. 325 if self.ordering:326 qs = qs.order_by(*self.ordering)336 ordering = self.get_ordering(request, qs) 337 qs = qs.order_by(*ordering) 327 338 328 339 # Apply keyword searches. 329 340 def construct_search(field_name): -
tests/regressiontests/admin_changelist/models.py
diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py index 97080fb..6b443bf 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 bool = models.BooleanField(default=True) 68 69 70 class OrderedObjectManager(models.Manager): 71 def get_query_set(self): 72 return super(OrderedObjectManager, self).get_query_set().order_by('-number') 73 74 class OrderedObject(models.Model): 75 """ 76 Model with Manager that defines a default order. 77 Refs #17198. 78 """ 79 name = models.CharField(max_length=255) 80 bool = models.BooleanField(default=True) 81 number = models.IntegerField(default=0) 82 83 objects = OrderedObjectManager() 84 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..7661cf7 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, OrderedObject) 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_deterministic_order(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 442 # --------------------------------------------------------------------- 443 # Firstly, when the Model doesn't have any default ordering defined 444 for counter in range(1, 51): 445 UnorderedObject.objects.create(id=counter, bool=True) 446 447 class UnorderedObjectAdmin(admin.ModelAdmin): 448 list_per_page = 10 449 450 def check_results_order(reverse=False): 451 admin.site.register(UnorderedObject, UnorderedObjectAdmin) 452 model_admin = UnorderedObjectAdmin(UnorderedObject, admin.site) 453 counter = 51 if reverse else 0 454 for page in range (0, 5): 455 request = self._mocked_authenticated_request('/unorderedobject/?p=%s' % page, superuser) 456 response = model_admin.changelist_view(request) 457 for result in response.context_data['cl'].result_list: 458 counter += -1 if reverse else 1 459 self.assertEqual(result.id, counter) 460 admin.site.unregister(UnorderedObject) 461 462 # When no order is defined at all, everything is ordered by 'pk'. 463 check_results_order() 464 465 # When an order field is defined but multiple records have the same 466 # value for that field, make sure everything gets ordered by pk as well. 467 UnorderedObjectAdmin.ordering = ['bool'] 468 check_results_order() 469 470 # When order fields are defined, including the pk itself, use them. 471 UnorderedObjectAdmin.ordering = ['bool', '-pk'] 472 check_results_order(reverse=True) 473 UnorderedObjectAdmin.ordering = ['bool', 'pk'] 474 check_results_order() 475 UnorderedObjectAdmin.ordering = ['-id', 'bool'] 476 check_results_order(reverse=True) 477 UnorderedObjectAdmin.ordering = ['id', 'bool'] 478 check_results_order() 479 480 # --------------------------------------------------------------------- 481 # Secondly, when the Model has a manager that defines a default ordering 482 for counter in range(1, 51): 483 OrderedObject.objects.create(id=counter, bool=True, number=counter) 484 485 class OrderedObjectAdmin(admin.ModelAdmin): 486 list_per_page = 10 487 488 def check_results_order(reverse=False): 489 admin.site.register(OrderedObject, OrderedObjectAdmin) 490 model_admin = OrderedObjectAdmin(OrderedObject, admin.site) 491 counter = 51 if reverse else 0 492 for page in range (0, 5): 493 request = self._mocked_authenticated_request('/orderedobject/?p=%s' % page, superuser) 494 response = model_admin.changelist_view(request) 495 for result in response.context_data['cl'].result_list: 496 counter += -1 if reverse else 1 497 self.assertEqual(result.id, counter) 498 admin.site.unregister(OrderedObject) 499 500 # When no order is defined at all, use the model's default ordering (i.e. '-number') 501 check_results_order(reverse=True) 502 503 # When an order field is defined but multiple records have the same 504 # value for that field, make sure everything gets ordered by pk as well. 505 OrderedObjectAdmin.ordering = ['bool'] 506 check_results_order() 507 508 # When order fields are defined, including the pk itself, use them. 509 OrderedObjectAdmin.ordering = ['bool', '-pk'] 510 check_results_order(reverse=True) 511 OrderedObjectAdmin.ordering = ['bool', 'pk'] 512 check_results_order() 513 OrderedObjectAdmin.ordering = ['-id', 'bool'] 514 check_results_order(reverse=True) 515 OrderedObjectAdmin.ordering = ['id', 'bool'] 516 check_results_order() 517 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..17533f9 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) -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index dc8f29a..9e12131 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 of 276 # the queryset should not be changed. 277 276 """ 277 If no ordering is defined in `ModelAdmin.ordering` or in the query 278 string, then the underlying order of the queryset should not be 279 changed, even if it is defined in `Modeladmin.queryset()`. 280 Refs #11868, #7309. 281 """ 278 282 p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80) 279 283 p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70) 280 284 p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60) … … class AdminViewListEditable(TestCase): 1881 1885 self.assertEqual(Category.objects.get(id=3).order, 1) 1882 1886 self.assertEqual(Category.objects.get(id=4).order, 0) 1883 1887 1888 def test_list_editable_ordering_default(self): 1889 """ 1890 Ensure that the list_editable items on the changelist are ordered 1891 in a deterministic order and without raising exceptions, even when 1892 no default ordering is explicitly defined. 1893 Refs #16819. 1894 """ 1895 UnorderedObject.objects.create(id=1, name='Unordered object #1') 1896 UnorderedObject.objects.create(id=2, name='Unordered object #2') 1897 UnorderedObject.objects.create(id=3, name='Unordered object #3') 1898 response = self.client.get('/test_admin/admin/admin_views/unorderedobject/') 1899 self.assertContains(response, 'Unordered object #1') 1900 self.assertContains(response, 'Unordered object #2') 1901 self.assertNotContains(response, 'Unordered object #3') 1902 response = self.client.get('/test_admin/admin/admin_views/unorderedobject/?p=1') 1903 self.assertNotContains(response, 'Unordered object #1') 1904 self.assertNotContains(response, 'Unordered object #2') 1905 self.assertContains(response, 'Unordered object #3') 1906 1884 1907 def test_list_editable_action_submit(self): 1885 1908 # List editable changes should not be executed if the action "Go" button is 1886 1909 # used to submit the form.