mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #11868 - Multiple sort in admin changelist.
Many thanks to bendavis78 for the initial patch, and for input from others. Also fixed #7309. If people were relying on the undocumented default ordering applied by the admin before, they will need to add 'ordering = ["-pk"]' to their ModelAdmin. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16316 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -67,11 +67,11 @@ class CustomUserAdmin(UserAdmin):
|
||||
|
||||
class BookAdmin(ModelAdmin):
|
||||
list_filter = ('year', 'author', 'contributors', 'is_best_seller', 'date_registered', 'no')
|
||||
order_by = '-id'
|
||||
ordering = ('-id',)
|
||||
|
||||
class DecadeFilterBookAdmin(ModelAdmin):
|
||||
list_filter = ('author', DecadeListFilterWithTitleAndParameter)
|
||||
order_by = '-id'
|
||||
ordering = ('-id',)
|
||||
|
||||
class DecadeFilterBookAdminWithoutTitle(ModelAdmin):
|
||||
list_filter = (DecadeListFilterWithoutTitle,)
|
||||
|
||||
@@ -243,9 +243,6 @@ class Person(models.Model):
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
ordering = ["id"]
|
||||
|
||||
class BasePersonModelFormSet(BaseModelFormSet):
|
||||
def clean(self):
|
||||
for person_dict in self.cleaned_data:
|
||||
@@ -259,13 +256,17 @@ class PersonAdmin(admin.ModelAdmin):
|
||||
list_editable = ('gender', 'alive')
|
||||
list_filter = ('gender',)
|
||||
search_fields = ('^name',)
|
||||
ordering = ["id"]
|
||||
save_as = True
|
||||
|
||||
def get_changelist_formset(self, request, **kwargs):
|
||||
return super(PersonAdmin, self).get_changelist_formset(request,
|
||||
formset=BasePersonModelFormSet, **kwargs)
|
||||
|
||||
def queryset(self, request):
|
||||
# Order by a field that isn't in list display, to be able to test
|
||||
# whether ordering is preserved.
|
||||
return super(PersonAdmin, self).queryset(request).order_by('age')
|
||||
|
||||
|
||||
class Persona(models.Model):
|
||||
"""
|
||||
@@ -357,6 +358,9 @@ class Media(models.Model):
|
||||
class Podcast(Media):
|
||||
release_date = models.DateField()
|
||||
|
||||
class Meta:
|
||||
ordering = ('release_date',) # overridden in PodcastAdmin
|
||||
|
||||
class PodcastAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'release_date')
|
||||
list_editable = ('release_date',)
|
||||
@@ -795,6 +799,7 @@ class StoryAdmin(admin.ModelAdmin):
|
||||
list_display_links = ('title',) # 'id' not in list_display_links
|
||||
list_editable = ('content', )
|
||||
form = StoryForm
|
||||
ordering = ["-pk"]
|
||||
|
||||
class OtherStory(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
@@ -804,6 +809,7 @@ class OtherStoryAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'title', 'content')
|
||||
list_display_links = ('title', 'id') # 'id' in list_display_links
|
||||
list_editable = ('content', )
|
||||
ordering = ["-pk"]
|
||||
|
||||
admin.site.register(Article, ArticleAdmin)
|
||||
admin.site.register(CustomArticle, CustomArticleAdmin)
|
||||
|
||||
@@ -32,7 +32,7 @@ from django.utils import unittest
|
||||
|
||||
# local test models
|
||||
from models import (Article, BarAccount, CustomArticle, EmptyModel,
|
||||
FooAccount, Gallery, GalleryAdmin, ModelWithStringPrimaryKey,
|
||||
FooAccount, Gallery, PersonAdmin, ModelWithStringPrimaryKey,
|
||||
Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast,
|
||||
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit,
|
||||
Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
|
||||
@@ -204,7 +204,7 @@ class AdminViewBasicTest(TestCase):
|
||||
Ensure we can sort on a list_display field that is a callable
|
||||
(column 2 is callable_year in ArticleAdmin)
|
||||
"""
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 2})
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': 2})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index('Oldest content') < response.content.index('Middle content') and
|
||||
@@ -217,7 +217,7 @@ class AdminViewBasicTest(TestCase):
|
||||
Ensure we can sort on a list_display field that is a Model method
|
||||
(colunn 3 is 'model_year' in ArticleAdmin)
|
||||
"""
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'dsc', 'o': 3})
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '-3'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index('Newest content') < response.content.index('Middle content') and
|
||||
@@ -230,7 +230,7 @@ class AdminViewBasicTest(TestCase):
|
||||
Ensure we can sort on a list_display field that is a ModelAdmin method
|
||||
(colunn 4 is 'modeladmin_year' in ArticleAdmin)
|
||||
"""
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 4})
|
||||
response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '4'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index('Oldest content') < response.content.index('Middle content') and
|
||||
@@ -238,6 +238,81 @@ class AdminViewBasicTest(TestCase):
|
||||
"Results of sorting on ModelAdmin method are out of order."
|
||||
)
|
||||
|
||||
def testChangeListSortingMultiple(self):
|
||||
p1 = Person.objects.create(name="Chris", gender=1, alive=True)
|
||||
p2 = Person.objects.create(name="Chris", gender=2, alive=True)
|
||||
p3 = Person.objects.create(name="Bob", gender=1, alive=True)
|
||||
link = '<a href="%s/'
|
||||
|
||||
# Sort by name, gender
|
||||
# This hard-codes the URL because it'll fail if it runs against the
|
||||
# 'admin2' custom admin (which doesn't have the Person model).
|
||||
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % p3.id) < response.content.index(link % p1.id) and
|
||||
response.content.index(link % p1.id) < response.content.index(link % p2.id)
|
||||
)
|
||||
|
||||
# Sort by gender descending, name
|
||||
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % p2.id) < response.content.index(link % p3.id) and
|
||||
response.content.index(link % p3.id) < response.content.index(link % p1.id)
|
||||
)
|
||||
|
||||
def testChangeListSortingPreserveQuerySetOrdering(self):
|
||||
# If no ordering on ModelAdmin, or query string, the underlying order of
|
||||
# the queryset should not be changed.
|
||||
|
||||
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)
|
||||
link = '<a href="%s/'
|
||||
|
||||
# This hard-codes the URL because it'll fail if it runs against the
|
||||
# 'admin2' custom admin (which doesn't have the Person model).
|
||||
response = self.client.get('/test_admin/admin/admin_views/person/', {})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % p3.id) < response.content.index(link % p2.id) and
|
||||
response.content.index(link % p2.id) < response.content.index(link % p1.id)
|
||||
)
|
||||
|
||||
def testChangeListSortingModelMeta(self):
|
||||
# Test ordering on Model Meta is respected
|
||||
|
||||
l1 = Language.objects.create(iso='ur', name='Urdu')
|
||||
l2 = Language.objects.create(iso='ar', name='Arabic')
|
||||
link = '<a href="%s/'
|
||||
|
||||
response = self.client.get('/test_admin/admin/admin_views/language/', {})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % l2.pk) < response.content.index(link % l1.pk)
|
||||
)
|
||||
|
||||
# Test we can override with query string
|
||||
response = self.client.get('/test_admin/admin/admin_views/language/', {'o':'-1'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % l1.pk) < response.content.index(link % l2.pk)
|
||||
)
|
||||
|
||||
def testChangeListSortingModelAdmin(self):
|
||||
# Test ordering on Model Admin is respected, and overrides Model Meta
|
||||
dt = datetime.datetime.now()
|
||||
p1 = Podcast.objects.create(name="A", release_date=dt)
|
||||
p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
|
||||
|
||||
link = '<a href="%s/'
|
||||
response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
response.content.index(link % p1.pk) < response.content.index(link % p2.pk)
|
||||
)
|
||||
|
||||
def testLimitedFilter(self):
|
||||
"""Ensure admin changelist filters do not contain objects excluded via limit_choices_to.
|
||||
This also tests relation-spanning filters (e.g. 'color__value').
|
||||
@@ -1956,7 +2031,7 @@ class AdminActionsTest(TestCase):
|
||||
'action' : 'external_mail',
|
||||
'index': 0,
|
||||
}
|
||||
url = '/test_admin/admin/admin_views/externalsubscriber/?ot=asc&o=1'
|
||||
url = '/test_admin/admin/admin_views/externalsubscriber/?o=1'
|
||||
response = self.client.post(url, action_data)
|
||||
self.assertRedirects(response, url)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user