Ticket #17522: r17364-admin-ordering-validation.diff

File r17364-admin-ordering-validation.diff, 6.7 KB (added by Sebastian Goll, 13 years ago)
  • docs/ref/contrib/admin/index.txt

     
    732732
    733733    Set ``ordering`` to specify how lists of objects should be ordered in the
    734734    Django admin views. This should be a list or tuple in the same format as a
    735     model's :attr:`~django.db.models.Options.ordering` parameter.
     735    model's :attr:`~django.db.models.Options.ordering` parameter. In addition
     736    to field names, ``ordering`` may contain the names of methods of the model
     737    or admin class which define the ``admin_order_field`` attribute (cf.
     738    :attr:`~ModelAdmin.list_display`).
    736739
    737740    If this isn't provided, the Django admin will use the model's default
    738741    ordering.
  • tests/regressiontests/admin_validation/tests.py

     
    44from django.contrib import admin
    55from django.contrib.admin.validation import validate, validate_inline
    66from django.core.exceptions import ImproperlyConfigured
     7from django.db.models import Count
    78from django.test import TestCase
    89
    910from .models import Song, Book, Album, TwoAlbumFKAndAnE, State, City
     
    281282            fields = ['extra_data', 'title']
    282283
    283284        validate(FieldsOnFormOnlyAdmin, Song)
     285
     286    def test_ordering_regular_field(self):
     287        """
     288        Ordering with proper field must succeed.
     289        """
     290        class SongAdmin(admin.ModelAdmin):
     291            ordering = ('title',)
     292        validate(SongAdmin, Song)
     293
     294    def test_ordering_nonexisting_field(self):
     295        """
     296        Ordering for an unknown field must fail.
     297        """
     298        class SongAdmin(admin.ModelAdmin):
     299            ordering = ('spam',)
     300        self.assertRaisesMessage(ImproperlyConfigured,
     301            "'SongAdmin.ordering[0]' refers to field 'spam' that is missing from model 'admin_validation.Song'.",
     302            validate,
     303            SongAdmin, Song)
     304
     305    def test_ordering_readonly_method(self):
     306        """
     307        Ordering for methods that do not specify admin_order_field
     308        must fail.
     309        """
     310        class ModelSongAdmin(admin.ModelAdmin):
     311            ordering = ('readonly_method_on_model',)
     312        self.assertRaisesMessage(ImproperlyConfigured,
     313            "'ModelSongAdmin.ordering[0]' refers to method 'readonly_method_on_model' with admin_order_field unset.",
     314            validate,
     315            ModelSongAdmin, Song)
     316
     317        class AdminSongAdmin(admin.ModelAdmin):
     318            ordering = ('readonly_method_on_admin',)
     319            def readonly_method_on_admin(self, song):
     320                pass
     321        self.assertRaisesMessage(ImproperlyConfigured,
     322            "'AdminSongAdmin.ordering[0]' refers to method 'readonly_method_on_admin' with admin_order_field unset.",
     323            validate,
     324            AdminSongAdmin, Song)
     325
     326    def test_ordering_sortable_method_field(self):
     327        """
     328        Ordering for methods with admin_order_field set to a proper
     329        field must succeed.
     330        """
     331        class ModelSongAdmin(admin.ModelAdmin):
     332            ordering = ('sortable_method_on_model',)
     333        validate(ModelSongAdmin, Song)
     334
     335        class AdminSongAdmin(admin.ModelAdmin):
     336            ordering = ('sortable_method_on_admin',)
     337            def sortable_method_on_admin(self, song):
     338                pass
     339            sortable_method_on_admin.admin_order_field = 'title'
     340        validate(AdminSongAdmin, Song)
     341
     342    def test_ordering_sortable_method_queryset(self):
     343        """
     344        Ordering for methods with admin_order_field set to name of
     345        an additional field (e.g. annotation) returned by a custom
     346        queryset must succeed.
     347        """
     348        class AlbumAdmin(admin.ModelAdmin):
     349            ordering = ('advanced_sortable_method_on_admin',)
     350            def queryset(self, request):
     351                return Album.objects.annotate(songs_count=Count('song_set'))
     352            def advanced_sortable_method_on_admin(self, album):
     353                pass
     354            advanced_sortable_method_on_admin.admin_order_field = 'songs_count'
     355        validate(AlbumAdmin, Album)
  • tests/regressiontests/admin_validation/models.py

     
    1111
    1212class Song(models.Model):
    1313    title = models.CharField(max_length=150)
    14     album = models.ForeignKey(Album)
     14    album = models.ForeignKey(Album, related_name="song_set")
    1515    original_release = models.DateField(editable=False)
    1616
    1717    class Meta:
     
    2424        # does nothing
    2525        pass
    2626
     27    def sortable_method_on_model(self):
     28        pass
     29    sortable_method_on_model.admin_order_field = 'title'
    2730
     31
    2832class TwoAlbumFKAndAnE(models.Model):
    2933    album1 = models.ForeignKey(Album, related_name="album1_set")
    3034    album2 = models.ForeignKey(Album, related_name="album2_set")
  • django/contrib/admin/validation.py

     
    154154                continue
    155155            if field.startswith('-'):
    156156                field = field[1:]
     157            # Check whether `field` is a name of a non-field that
     158            # allows for sorting, i.e. name of a method on either
     159            # the admin cls or model with admin_order_field set.
     160            attr = (getattr(cls, field, None) or
     161                    getattr(model, field, None))
     162            if callable(attr):
     163                if not hasattr(attr, 'admin_order_field'):
     164                    # Found method but no admin_order_field.
     165                    raise ImproperlyConfigured("'%s.%s' refers "
     166                            "to method '%s' with admin_order_field unset."
     167                            % (cls.__name__, 'ordering[%d]' % idx, field))
     168                # No further checks for methods with
     169                # admin_order_field. In particular, we do not enforce
     170                # admin_order_field to refer to an actual model field:
     171                # it might also be an annotation returned by the model
     172                # admin's custom queryset() method etc.
     173                continue
    157174            # Skip ordering in the format field1__field2 (FIXME: checking
    158175            # this format would be nice, but it's a little fiddly).
    159176            if '__' in field:
Back to Top