Определение пользовательского списка приложений на странице индекса администратора django

Я хотел бы указать список настраиваемых приложений, который будет использоваться на странице индекса администратора django, потому что я хочу, чтобы приложения отображались в определенном порядке, а не в алфавитном порядке по умолчанию. Травля через различные сообщения SO, казалось бы, пока невозможно объявить желаемый порядок приложений в любом из очевидных мест (например, admin.py, models.py).

Теперь я вижу, что файл django admin index.html содержит следующую инструкцию:

{% for app in app_list %}
   # do stuff with the app object

Поэтому я хотел бы изменить это, чтобы использовать пользовательский объект списка, называемый, например, my_app_list. В python я бы сделал это в следующих строках:

from django.db.models import get_app
my_app_list = [get_app('myapp1'), get_app('myapp2'), ..., get_app('django.contrib.auth')]
for app in my_app_list
   ...

Тогда мой вопрос: как мне закодировать эквивалент первых 2 строк выше в моей локальной копии файла index.html?

Или, в качестве альтернативы, какой исходный файл python должен вставлять эти строки так, чтобы переменная my_app_list была доступна в index.html.

Спасибо заранее.

Фил

Ответ 1

Подкласс django.contrib.admin.site.AdminSite(). Переопределите метод .index() и выполните следующие действия:

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)

Создайте экземпляр этого подкласса с помощью my_admin_site = MyAdminSite(), присоедините свои модели к нему (используя обычный my_admin_site.register()) и присоедините его к URLconf; это должно сделать это.

(Я не пробовал это, я основываю это на чтении источника AdminSite.)

Ответ 2

Если вы не возражаете использовать подкласс django.contrib.admin.site.AdminSite(), как и ожидалось в случаях, когда вам нужно настроить свой сайт администратора, Я думаю, что это приемлемая идея, переписывающая методы "index" и "app_index" в производном классе. Вы можете сделать заказ, используя два словаря, которые сохраняют порядок объявления объявлений в settings.py и порядок регистрации моделей. Затем перепишите код оригинала AdminSite().index() и app_index(), добавив пользовательские поля порядка ('order') в app_list и упорядочивая это поле, несмотря на 'name'. Это код, исключая app_index(), который похож на функцию index():

class MyAdminSite(AdminSite):

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

        # Model registration ordering. It 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)

Если вы используете пользовательский AdminSite и хотите включить Auth-модели, вам, вероятно, понадобится это, где-то в вашем коде (я сделал это в определенном приложении для расширить пользователя информация:

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

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

Ответ 3

После выполнения того, что @AdminKG сказал, скопируйте файл index.html в корень каталога admin, который вам нужно создать внутри каталога templates, который вы указали на вас setting.py.

если у вас есть четкая логика сортировки для app_list, вы можете реализовать ее в методе .index() вашего подкласса AdminSite. В противном случае вам нужно будет жестко закодировать список приложений на index.html.

Чтобы получить доступ к чему-то в вашем шаблоне, просто используйте его в своем контексте, что-то вроде этого:

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
    )

Теперь объекты приложений доступны для использования на index.htm

Ответ 4

Поскольку вы беспокоитесь о заказе, вы можете найти мое решение полезным.
В принципе, я создал фильтр, который перемещает нужные элементы app_list в начало.

@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

Однако, если вам нужно удалить некоторые элементы, вы можете использовать:

@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]

Фильтры могут быть скованы цепью, поэтому вы можете использовать оба одновременно.
Функции фильтрации не записываются так, чтобы они ускоряли демоны, но этот шаблон не слишком часто отображается по определению.

Ответ 5

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

применить любой вид на app_list