7

I want to order models in my Django admin interface. The default is alphabetical ordering of models within apps.

I would like to avoid creating extra packages etc, etc, or having to alter Django itself.

Can this be done by overriding Django admin templates, or some other, lightweight way?

EDIT:

I dont want to order final items, like a list of todos.

I want to order different models in index page of admin. E.g. todos come before cars.

tonino.j
  • 3,837
  • 28
  • 27

6 Answers6

4

You can override the template and add a templatetag to sort. Here's a snippet, it might need some alteration http://djangosnippets.org/snippets/1939/

Hedde van der Heide
  • 21,841
  • 13
  • 71
  • 100
  • As I said, I dont want to sort a list of todos (for example), but I want a link to todos list (in admin index page) to come before soem other model. This seems like it may do the trick, but it isn't exactly lightweight. Anyway, upvote. I'll check it. – tonino.j Dec 23 '12 at 09:45
3

I don't think there's a lightweight way of doing it - for one thing there's no facility to store the ordering, so it will at least involve a new database field somewhere, and then code that gets that field to find the required ordering. Actually that might not work because the AdminSite is constructed dynamically, and not stored in the database... Hmm, let's see what Django does...

The index template is just a loop over admin-registered apps and within each app a loop over models. The ordering of those is defined by lines 384 onwards in https://github.com/django/django/blob/master/django/contrib/admin/sites.py - so one hook would be to subclass AdminSite and rewrite the index method. Making new AdminSite things is talked about here: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#adminsite-objects

Perhaps if you want to present the models in the order they are registered you could just take out the sorting lines in that method.

Spacedman
  • 92,590
  • 12
  • 140
  • 224
3

I'm leaving a not-great answer.

The models get sorted on line 448 in django/contrib/admin/sites.py

448        # Sort the models alphabetically within each app.
449        for app in app_list:
450            app['models'].sort(key=lambda x: x['name'])

I have not tested this out, but you could try adding a variable to the model's Meta class that you would use to set the order of the models in the admin's index page in the for loop on line 403.

403        for model, model_admin in self._registry.items():
               ,,,
431                    else:
432                        order = model._meta.order
433                        app_dict[app_label] = {
434                            'order': order,
435                            ...

And replace the sort with the following:

450            app['models'].sort(key=lambda x: x['order'])
johnklawlor
  • 1,708
  • 2
  • 13
  • 15
  • 2
    That's the correct way. We just need to custom the `AdminSite` like [this](https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#customizing-the-adminsite-class), then rewrite the `get_app_list` method to apply our sorting algorism. – Kxrr Mar 29 '17 at 08:36
2

I just did this with proxy models so that models could be grouped under a single heading. I realized that you can adjust the sort order by just changing the verbose_name and verbose_name_plural of the proxy model. So a top level name could be achieved with " " + ._meta.verbose_name and others could just be reordered by 1., 2. 3., ...

I'm sure this is not what you're looking for, but this was as light weight as I could achieve with acceptable results.

# foo/admin.py
from django.contrib import admin
from zoo.models import Zoo

class ZooProxy(Zoo):
    class Meta:
        proxy = True
        app_label = 'foo'
        verbose_name = " " + Zoo._meta.verbose_name
        verbose_name_plural = " " + Zoo._meta.verbose_name_plural


# one/admin.py
from django.contrib import admin
from one.models import One

class OneProxy(One):
    class Meta:
        proxy = True
        app_label = 'foo'
        verbose_name = "2. " + One._meta.verbose_name
        verbose_name_plural = "2. " + One._meta.verbose_name_plural


# two/admin.py
from django.contrib import admin
from two.models import Two

class TwoProxy(Two):
    class Meta:
        proxy = True
        app_label = 'foo'
        verbose_name = "1. " + Two._meta.verbose_name
        verbose_name_plural = "1. " + Two._meta.verbose_name_plural

result in django admin:

Foo:
    Zoo
    1. Two
    2. One

-vs-

Foo:
    One
    Two
    Zoo
imdaveho
  • 101
  • 8
0

Since my code registers the models one by one using admin.site.register, it would have been nice if Django could remember and use that order. Changing a few dictionaries in django/contrib/admin/sites.py to OrderedDict() would probably provide that functionality.

This can be accomplished by overriding the default admin site and making a few small changes:

class MyAdminSite(AdminSite):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._registry = OrderedDict()  # remembers the registration order

    def get_app_list(self, request):
        app_dict = self._build_app_dict(request)

        # Sort the apps alphabetically.
        app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())

        # disabled the sorting of models
        ## Sort the models alphabetically within each app.
        #for app in app_list:
        #    app['models'].sort(key=lambda x: x['name'])

        return app_list
Ramon
  • 89
  • 1
  • 3
-3

I am not sure if Django provides a default sorting however you can use ModelAdmin objects to add a filter and sort pane in Django admin. It looks something like this.enter image description here

These changes will have to be done in admin.py file

class PersonAdmin(ModelAdmin):
    list_filter = ('is_staff', 'company')

list_filter will add that pane which can serve your purpose of sorting and filtering

For more, look at this: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

pass-by-ref
  • 1,288
  • 2
  • 12
  • 20