diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 712f07eec7d..7a42e9444cc 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -67,6 +67,7 @@ class BaseModelAdmin(object): prepopulated_fields = {} formfield_overrides = {} readonly_fields = () + ordering = None def __init__(self): overrides = FORMFIELD_FOR_DBFIELD_DEFAULTS.copy() @@ -189,6 +190,18 @@ class BaseModelAdmin(object): def get_readonly_fields(self, request, obj=None): return self.readonly_fields + def queryset(self, request): + """ + Returns a QuerySet of all model instances that can be edited by the + admin site. This is used by changelist_view. + """ + qs = self.model._default_manager.get_query_set() + # TODO: this should be handled by some parameter to the ChangeList. + ordering = self.ordering or () # otherwise we might try to *None, which is bad ;) + if ordering: + qs = qs.order_by(*ordering) + return qs + class ModelAdmin(BaseModelAdmin): "Encapsulates all admin options and functionality for a given model." @@ -202,7 +215,6 @@ class ModelAdmin(BaseModelAdmin): date_hierarchy = None save_as = False save_on_top = False - ordering = None inlines = [] # Custom templates (designed to be over-ridden in subclasses) @@ -325,18 +337,6 @@ class ModelAdmin(BaseModelAdmin): 'delete': self.has_delete_permission(request), } - def queryset(self, request): - """ - Returns a QuerySet of all model instances that can be edited by the - admin site. This is used by changelist_view. - """ - qs = self.model._default_manager.get_query_set() - # TODO: this should be handled by some parameter to the ChangeList. - ordering = self.ordering or () # otherwise we might try to *None, which is bad ;) - if ordering: - qs = qs.order_by(*ordering) - return qs - def get_fieldsets(self, request, obj=None): "Hook for specifying fieldsets for the add form." if self.declared_fieldsets: @@ -1257,9 +1257,6 @@ class InlineModelAdmin(BaseModelAdmin): fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) return [(None, {'fields': fields})] - def queryset(self, request): - return self.model._default_manager.all() - class StackedInline(InlineModelAdmin): template = 'admin/edit_inline/stacked.html' diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 7d650f52baa..9928118fd98 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -180,7 +180,7 @@ subclass:: .. versionadded:: 1.2 ``fields`` can contain values defined in - :attr:`ModelAdmin.readonly_fields` to be displayed as read-only. + :attr:`~ModelAdmin.readonly_fields` to be displayed as read-only. * ``classes`` A list containing extra CSS classes to apply to the fieldset. @@ -504,8 +504,8 @@ subclass:: .. attribute:: ModelAdmin.ordering - Set ``ordering`` to specify how objects on the admin change list page - should be ordered. This should be a list or tuple in the same format as a + Set ``ordering`` to specify how lists of objects should be ordered in the + Django admin views. This should be a list or tuple in the same format as a model's ``ordering`` parameter. If this isn't provided, the Django admin will use the model's default @@ -1089,8 +1089,36 @@ information. ``InlineModelAdmin`` options ----------------------------- -The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits -all the same functionality as well as some of its own: +``InlineModelAdmin`` shares many of the same features as ``ModelAdmin``, and +adds some of its own (the shared features are actually defined in the +``BaseModelAdmin`` superclass). The shared features are: + +- :attr:`~InlineModelAdmin.form` +- :attr:`~ModelAdmin.fieldsets` +- :attr:`~ModelAdmin.fields` +- :attr:`~ModelAdmin.exclude` +- :attr:`~ModelAdmin.filter_horizontal` +- :attr:`~ModelAdmin.filter_vertical` +- :attr:`~ModelAdmin.prepopulated_fields` +- :attr:`~ModelAdmin.radio_fields` +- :attr:`~InlineModelAdmin.raw_id_fields` + +.. versionadded:: 1.1 + +- :meth:`~ModelAdmin.formfield_for_foreignkey` +- :meth:`~ModelAdmin.formfield_for_manytomany` + +.. versionadded:: 1.2 + +- :attr:`~ModelAdmin.readonly_fields` +- :attr:`~ModelAdmin.formfield_overrides` + +.. versionadded:: 1.3 + +- :attr:`~ModelAdmin.ordering` +- :meth:`~ModelAdmin.queryset` + +The ``InlineModelAdmin`` class adds: .. attribute:: InlineModelAdmin.model @@ -1118,7 +1146,6 @@ all the same functionality as well as some of its own: .. attribute:: InlineModelAdmin.extra - This controls the number of extra forms the formset will display in addition to the initial forms. See the :doc:`formsets documentation ` for more diff --git a/tests/regressiontests/admin_ordering/models.py b/tests/regressiontests/admin_ordering/models.py index ad63685b2f3..fb766777a9d 100644 --- a/tests/regressiontests/admin_ordering/models.py +++ b/tests/regressiontests/admin_ordering/models.py @@ -1,5 +1,6 @@ # coding: utf-8 from django.db import models +from django.contrib import admin class Band(models.Model): name = models.CharField(max_length=100) @@ -8,3 +9,18 @@ class Band(models.Model): class Meta: ordering = ('name',) + +class Song(models.Model): + band = models.ForeignKey(Band) + name = models.CharField(max_length=100) + duration = models.IntegerField() + + class Meta: + ordering = ('name',) + +class SongInlineDefaultOrdering(admin.StackedInline): + model = Song + +class SongInlineNewOrdering(admin.StackedInline): + model = Song + ordering = ('duration', ) diff --git a/tests/regressiontests/admin_ordering/tests.py b/tests/regressiontests/admin_ordering/tests.py index f63f202ce16..8f5a6bd05ab 100644 --- a/tests/regressiontests/admin_ordering/tests.py +++ b/tests/regressiontests/admin_ordering/tests.py @@ -1,7 +1,7 @@ from django.test import TestCase from django.contrib.admin.options import ModelAdmin -from models import Band +from models import Band, Song, SongInlineDefaultOrdering, SongInlineNewOrdering class TestAdminOrdering(TestCase): """ @@ -37,3 +37,37 @@ class TestAdminOrdering(TestCase): ma = BandAdmin(Band, None) names = [b.name for b in ma.queryset(None)] self.assertEqual([u'Radiohead', u'Van Halen', u'Aerosmith'], names) + +class TestInlineModelAdminOrdering(TestCase): + """ + Let's make sure that InlineModelAdmin.queryset uses the ordering we define + in InlineModelAdmin. + """ + + def setUp(self): + b = Band(name='Aerosmith', bio='', rank=3) + b.save() + self.b = b + s1 = Song(band=b, name='Pink', duration=235) + s1.save() + s2 = Song(band=b, name='Dude (Looks Like a Lady)', duration=264) + s2.save() + s3 = Song(band=b, name='Jaded', duration=214) + s3.save() + + def test_default_ordering(self): + """ + The default ordering should be by name, as specified in the inner Meta + class. + """ + inline = SongInlineDefaultOrdering(self.b, None) + names = [s.name for s in inline.queryset(None)] + self.assertEqual([u'Dude (Looks Like a Lady)', u'Jaded', u'Pink'], names) + + def test_specified_ordering(self): + """ + Let's check with ordering set to something different than the default. + """ + inline = SongInlineNewOrdering(self.b, None) + names = [s.name for s in inline.queryset(None)] + self.assertEqual([u'Jaded', u'Pink', u'Dude (Looks Like a Lady)'], names) \ No newline at end of file