summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/regressiontests/admin_changelist/models.py24
-rw-r--r--tests/regressiontests/admin_changelist/tests.py92
-rw-r--r--tests/regressiontests/admin_views/admin.py9
-rw-r--r--tests/regressiontests/admin_views/models.py11
-rwxr-xr-xtests/regressiontests/admin_views/tests.py29
5 files changed, 158 insertions, 7 deletions
diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py
index 97080fb548..6b443bf536 100644
--- a/tests/regressiontests/admin_changelist/models.py
+++ b/tests/regressiontests/admin_changelist/models.py
@@ -57,3 +57,27 @@ class Swallow(models.Model):
class Meta:
ordering = ('speed', 'load')
+
+
+class UnorderedObject(models.Model):
+ """
+ Model without any defined `Meta.ordering`.
+ Refs #17198.
+ """
+ bool = models.BooleanField(default=True)
+
+
+class OrderedObjectManager(models.Manager):
+ def get_query_set(self):
+ return super(OrderedObjectManager, self).get_query_set().order_by('-number')
+
+class OrderedObject(models.Model):
+ """
+ Model with Manager that defines a default order.
+ Refs #17198.
+ """
+ name = models.CharField(max_length=255)
+ bool = models.BooleanField(default=True)
+ number = models.IntegerField(default=0)
+
+ objects = OrderedObjectManager() \ No newline at end of file
diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py
index b422519bfb..d2b017b258 100644
--- a/tests/regressiontests/admin_changelist/tests.py
+++ b/tests/regressiontests/admin_changelist/tests.py
@@ -14,7 +14,8 @@ from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
FilteredChildAdmin, CustomPaginator, site as custom_site,
SwallowAdmin)
from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
- Membership, ChordsMusician, ChordsBand, Invitation, Swallow)
+ Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
+ UnorderedObject, OrderedObject)
class ChangeListTests(TestCase):
@@ -430,3 +431,92 @@ class ChangeListTests(TestCase):
self.assertContains(response, unicode(swallow.load))
self.assertContains(response, unicode(swallow.speed))
+ def test_deterministic_order_for_unordered_model(self):
+ """
+ Ensure that the primary key is systematically used in the ordering of
+ the changelist's results to guarantee a deterministic order, even
+ when the Model doesn't have any default ordering defined.
+ Refs #17198.
+ """
+ superuser = self._create_superuser('superuser')
+
+ for counter in range(1, 51):
+ UnorderedObject.objects.create(id=counter, bool=True)
+
+ class UnorderedObjectAdmin(admin.ModelAdmin):
+ list_per_page = 10
+
+ def check_results_order(reverse=False):
+ admin.site.register(UnorderedObject, UnorderedObjectAdmin)
+ model_admin = UnorderedObjectAdmin(UnorderedObject, admin.site)
+ counter = 51 if reverse else 0
+ for page in range (0, 5):
+ request = self._mocked_authenticated_request('/unorderedobject/?p=%s' % page, superuser)
+ response = model_admin.changelist_view(request)
+ for result in response.context_data['cl'].result_list:
+ counter += -1 if reverse else 1
+ self.assertEqual(result.id, counter)
+ admin.site.unregister(UnorderedObject)
+
+ # When no order is defined at all, everything is ordered by 'pk'.
+ check_results_order()
+
+ # When an order field is defined but multiple records have the same
+ # value for that field, make sure everything gets ordered by pk as well.
+ UnorderedObjectAdmin.ordering = ['bool']
+ check_results_order()
+
+ # When order fields are defined, including the pk itself, use them.
+ UnorderedObjectAdmin.ordering = ['bool', '-pk']
+ check_results_order(reverse=True)
+ UnorderedObjectAdmin.ordering = ['bool', 'pk']
+ check_results_order()
+ UnorderedObjectAdmin.ordering = ['-id', 'bool']
+ check_results_order(reverse=True)
+ UnorderedObjectAdmin.ordering = ['id', 'bool']
+ check_results_order()
+
+ def test_deterministic_order_for_model_ordered_by_its_manager(self):
+ """
+ Ensure that the primary key is systematically used in the ordering of
+ the changelist's results to guarantee a deterministic order, even
+ when the Model has a manager that defines a default ordering.
+ Refs #17198.
+ """
+ superuser = self._create_superuser('superuser')
+
+ for counter in range(1, 51):
+ OrderedObject.objects.create(id=counter, bool=True, number=counter)
+
+ class OrderedObjectAdmin(admin.ModelAdmin):
+ list_per_page = 10
+
+ def check_results_order(reverse=False):
+ admin.site.register(OrderedObject, OrderedObjectAdmin)
+ model_admin = OrderedObjectAdmin(OrderedObject, admin.site)
+ counter = 51 if reverse else 0
+ for page in range (0, 5):
+ request = self._mocked_authenticated_request('/orderedobject/?p=%s' % page, superuser)
+ response = model_admin.changelist_view(request)
+ for result in response.context_data['cl'].result_list:
+ counter += -1 if reverse else 1
+ self.assertEqual(result.id, counter)
+ admin.site.unregister(OrderedObject)
+
+ # When no order is defined at all, use the model's default ordering (i.e. '-number')
+ check_results_order(reverse=True)
+
+ # When an order field is defined but multiple records have the same
+ # value for that field, make sure everything gets ordered by pk as well.
+ OrderedObjectAdmin.ordering = ['bool']
+ check_results_order()
+
+ # When order fields are defined, including the pk itself, use them.
+ OrderedObjectAdmin.ordering = ['bool', '-pk']
+ check_results_order(reverse=True)
+ OrderedObjectAdmin.ordering = ['bool', 'pk']
+ check_results_order()
+ OrderedObjectAdmin.ordering = ['-id', 'bool']
+ check_results_order(reverse=True)
+ OrderedObjectAdmin.ordering = ['id', 'bool']
+ check_results_order() \ No newline at end of file
diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
index b10d178640..d9607496c3 100644
--- a/tests/regressiontests/admin_views/admin.py
+++ b/tests/regressiontests/admin_views/admin.py
@@ -26,7 +26,8 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping,
Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
- AdminOrderedCallable, Report, Color2, MainPrepopulated, RelatedPrepopulated)
+ AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
+ RelatedPrepopulated)
def callable_year(dt_value):
@@ -562,6 +563,11 @@ class MainPrepopulatedAdmin(admin.ModelAdmin):
'slug2': ['status', 'name']}
+class UnorderedObjectAdmin(admin.ModelAdmin):
+ list_display = ['name']
+ list_editable = ['name']
+ list_per_page = 2
+
site = admin.AdminSite(name="admin")
@@ -609,6 +615,7 @@ site.register(Story, StoryAdmin)
site.register(OtherStory, OtherStoryAdmin)
site.register(Report, ReportAdmin)
site.register(MainPrepopulated, MainPrepopulatedAdmin)
+site.register(UnorderedObject, UnorderedObjectAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
index cf2896f60a..17533f9f80 100644
--- a/tests/regressiontests/admin_views/models.py
+++ b/tests/regressiontests/admin_views/models.py
@@ -596,4 +596,13 @@ class RelatedPrepopulated(models.Model):
choices=(('option one', 'Option One'),
('option two', 'Option Two')))
slug1 = models.SlugField(max_length=50)
- slug2 = models.SlugField(max_length=60) \ No newline at end of file
+ slug2 = models.SlugField(max_length=60)
+
+
+class UnorderedObject(models.Model):
+ """
+ Model without any defined `Meta.ordering`.
+ Refs #16819.
+ """
+ name = models.CharField(max_length=255)
+ bool = models.BooleanField(default=True)
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 4069c99ef6..5241d263a7 100755
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -12,6 +12,7 @@ from django.core.exceptions import SuspiciousOperation
from django.core.files import temp as tempfile
from django.core.urlresolvers import reverse
# Register auth models with the admin.
+from django.contrib import admin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.contrib.admin.models import LogEntry, DELETION
from django.contrib.admin.sites import LOGIN_FORM_KEY
@@ -41,7 +42,7 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story,
OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField,
AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
- Report, MainPrepopulated, RelatedPrepopulated)
+ Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject)
ERROR_MESSAGE = "Please enter the correct username and password \
@@ -272,9 +273,12 @@ class AdminViewBasicTest(TestCase):
)
def testChangeListSortingPreserveQuerySetOrdering(self):
- # If no ordering on ModelAdmin, or query string, the underlying order of
- # the queryset should not be changed.
-
+ """
+ If no ordering is defined in `ModelAdmin.ordering` or in the query
+ string, then the underlying order of the queryset should not be
+ changed, even if it is defined in `Modeladmin.queryset()`.
+ Refs #11868, #7309.
+ """
p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80)
p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70)
p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60)
@@ -1881,6 +1885,23 @@ class AdminViewListEditable(TestCase):
self.assertEqual(Category.objects.get(id=3).order, 1)
self.assertEqual(Category.objects.get(id=4).order, 0)
+ def test_list_editable_pagination(self):
+ """
+ Ensure that pagination works for list_editable items.
+ Refs #16819.
+ """
+ UnorderedObject.objects.create(id=1, name='Unordered object #1')
+ UnorderedObject.objects.create(id=2, name='Unordered object #2')
+ UnorderedObject.objects.create(id=3, name='Unordered object #3')
+ response = self.client.get('/test_admin/admin/admin_views/unorderedobject/')
+ self.assertContains(response, 'Unordered object #1')
+ self.assertContains(response, 'Unordered object #2')
+ self.assertNotContains(response, 'Unordered object #3')
+ response = self.client.get('/test_admin/admin/admin_views/unorderedobject/?p=1')
+ self.assertNotContains(response, 'Unordered object #1')
+ self.assertNotContains(response, 'Unordered object #2')
+ self.assertContains(response, 'Unordered object #3')
+
def test_list_editable_action_submit(self):
# List editable changes should not be executed if the action "Go" button is
# used to submit the form.