[Fixed]-Defining a custom app_list in django admin index page

9👍

Subclass django.contrib.admin.site.AdminSite(). Override the .index() method, and do something like this:

class MyAdminSite(django.contrib.admin.site.AdminSite):
    def index(self, request, extra_context=None):
        if extra_context is None:
            extra_context = {}
        extra_context["app_list"] = get_app_list_in_custom_order()
        return super(MyAdminSite, self).index(request, extra_context)

Instantiate an instance of this subclass with my_admin_site = MyAdminSite(), attach your models to it (using the usual my_admin_site.register()), and attach it to the URLconf; that should do it.

(I haven’t tried this, I’m basing this on my reading of the AdminSite source.)

👤AdamKG

2👍

If you don’t mind to use a subclass of django.contrib.admin.site.AdminSite(), as expected in cases when you need to customize your admin site, I think it’s a feasible idea rewriting “index” and “app_index” methods in the derived class. You can do custom ordering using two dictionaries that store the app declararion order in settings.py and the registration order of models.
Then rewrite the code of the original AdminSite().index() and app_index(), adding a custom order fields ('order') in app_list and order by this field despite 'name'. This is the code, excluding app_index(), that is similar to index() function:

class MyAdminSite(AdminSite):

    def __init__(self, name='admin', app_name='admin'):
        super(MyAdminSite, self).__init__(name, app_name)

        # Model's registration ordering. It's not necessary to
        # categorize by app.
        self._registry_ord = {}

        # App ordering determined by declaration
        self._app_ord = { 'auth' : 0 }
        app_position = 1
        for app in settings.INSTALLED_APPS:
            self._app_ord[app] = app_position
            app_position += 1

    def register(self, model_or_iterable, admin_class=None, **options):
        super(MyAdminSite, self).register(model_or_iterable, admin_class, **options)

        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model in self._registry:
                if self._registry_ord:
                    self._registry_ord[model._meta.object_name] = max(self._registry_ord.values()) + 1 
                else:
                    self._registry_ord[model._meta.object_name] = 1

    @never_cache
    def index(self, request, extra_context=None):
        """
        Displays the main admin index page, which lists all of the installed
        apps that have been registered in this site.
        """
        app_dict = {}
        user = request.user
        for model, model_admin in self._registry.items():
            app_label = model._meta.app_label
            has_module_perms = user.has_module_perms(app_label)

            if has_module_perms:
                perms = model_admin.get_model_perms(request)

                # Check whether user has any perm for this module.
                # If so, add the module to the model_list.
                if True in perms.values():
                    info = (app_label, model._meta.module_name)
                    model_dict = {
                        'name': capfirst(model._meta.verbose_name_plural),
                        'perms': perms,
                        'order': self._registry_ord[model._meta.object_name]
                    }
                    if perms.get('change', False):
                        try:
                            model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
                        except NoReverseMatch:
                            pass
                    if perms.get('add', False):
                        try:
                            model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
                        except NoReverseMatch:
                            pass
                    if app_label in app_dict:
                        app_dict[app_label]['models'].append(model_dict)
                    else:
                        app_dict[app_label] = {
                            'name': app_label.title(),
                            'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
                            'has_module_perms': has_module_perms,
                            'models': [model_dict],
                            'order': self._app_ord[app_label],
                        }

        # Sort the apps alphabetically.
        app_list = app_dict.values()
        app_list.sort(key=lambda x: x['order'])

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

        context = {
            'title': _('Site administration'),
            'app_list': app_list,
        }
        context.update(extra_context or {})
        return TemplateResponse(request, [
            self.index_template or 'admin/index.html',
        ], context, current_app=self.name)

If you use custom AdminSite and you want to include Auth models you probably need this, somewhere in your code (I made it in a specific app to extend user information :

from django.contrib.auth.models import User, Group
from myproject import admin

admin.site.register(User)
admin.site.register(Group)

0👍

After doing what @AdminKG said copy the index.html file to the root of the admin directory that you need to create inside the templates directory you declared on you setting.py.

if you you have a clear sorting logic for app_list you can implement it in the .index() method of your AdminSite‘s subclass. Otherwise you will need to hard code the app list on index.html.

To access something in your template just have it in your context, something like that:

def index(self, request, extra_context=None):
    context = {
        'app1':get_app('myappname'),
        'app2': get_app('mysecondappname'),
        # ...
    }
    context.update(extra_context or {})
    context_instance = template.RequestContext(request, current_app=self.name)
    return render_to_response(self.index_template or 'admin/terminal_index.html', context,
        context_instance=context_instance
    )

Now apps objects are available to use on your index.htm

0👍

Since you are concerned about the order, you can find my solution helpful.
Basically, I created a filter, which moves desired elements of app_list to the beginning.

@register.filter
def put_it_first(value, arg):
    '''The filter shifts specified items in app_list to the top,
    the syntax is: LIST_TO_PROCESS|put_it_first:"1st_item[;2nd_item...]"
    '''
    def _cust_sort(x):
        try:
            return arg.index(x['name'].lower())
        except ValueError:
            return dist
    arg = arg.split(';')
    arg = map(unicode.lower, arg)
    dist = len(arg) + 1
    value.sort(key=_cust_sort)
    return value

However, if you need to remove some elements you can use:

@register.filter
def remove_some(value, arg):
    '''The filter removes specified items from app_list,
    the syntax is: LIST_TO_PROCESS|remove_some:"1st_item[;2nd_item...]"
    '''
    arg = arg.split(';')
    arg = map(unicode.lower, arg)
    return [v for v in value if v['name'].lower() not in arg]

Filters can be chained, so you can use both at the same time.
Filtering functions are not written the way which would make them speed demons, but this template is not being rendered too often by definition.

👤Lukasz

0👍

app_list = admin.site.get_app_list(context['request'])

apply any sort on app_list

Leave a comment