Подтвердить что ты не робот

Заказ объектов admin.ModelAdmin

Скажем, у меня есть приложение для пиццы с классами Topping и Pizza, и они отображаются в Django Admin следующим образом:

PizzaApp
-
Toppings      >>>>>>>>>>      Add / Change

Pizzas        >>>>>>>>>>      Add / Change

Но я хочу, чтобы они были такими:

PizzaApp
-
Pizzas        >>>>>>>>>>      Add / Change

Toppings      >>>>>>>>>>      Add / Change

Как мне настроить это в моей admin.py?

4b9b3361

Ответ 1

Это фактически покрыто в самом низу часть 2 учебника Django.

Здесь соответствующий раздел:

Настроить страницу индекса администратора

Аналогичным образом вы можете захотеть настроить внешний вид Страница индекса администратора Django.

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

Шаблон для настройки админ /index.html. (Сделайте то же самое, что и с admin/base_site.html в предыдущем раздел - скопировать его по умолчанию в свой шаблон.) Отредактируйте файл, и вы см. в нем используется шаблонная переменная, называемая app_list. Эта переменная содержит все установленное приложение Django. Вместо того, чтобы использовать что вы можете жестко закодировать ссылки на объектные страницы администратора в как вам кажется лучше всего.

Ответ 2

Обходной путь, который вы можете попробовать, это настроить ваши модели .py следующим образом:

class Topping(models.Model):
    .
    .
    .
    class Meta:
        verbose_name_plural = "2. Toppings"

class Pizza(models.Model):
    .
    .
    .
    class Meta:
        verbose_name_plural = "1. Pizzas"

Не уверен, что это против лучших практик django, но он работает (протестирован с django trunk).

Удачи!

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

Ответ 3

Если вы хотите решить это за 10 секунд, просто используйте пробелы в verbose_name_plural, например:

class Topping(models.Model):
    class Meta:
        verbose_name_plural = "  Toppings" # 2 spaces

class Pizza(models.Model):
    class Meta:
        verbose_name_plural = " Pizzas" # 1 space

Конечно, это не ellegant, но может работать некоторое время, прежде чем мы получим лучшее решение.

Ответ 4

В итоге мне удалось сделать это благодаря фрагменту Django, вам просто нужно знать настройку ADMIN_REORDER:

ADMIN_REORDER = (
    ('app1', ('App1Model1', 'App1Model2', 'App1Model3')),
    ('app2', ('App2Model1', 'App2Model2')),
)

app1 не должен иметь префикс имени проекта, т.е. использовать app1 вместо mysite.app1.

Ответ 6

Здесь используется фрагмент Emmanuel, обновленный для Django 1.8:

В templatetags/admin_reorder.py:

from django import template
from django.conf import settings
from collections import OrderedDict

register = template.Library()

# from http://www.djangosnippets.org/snippets/1937/
def register_render_tag(renderer):
    """
    Decorator that creates a template tag using the given renderer as the 
    render function for the template tag node - the render function takes two 
    arguments - the template context and the tag token
    """
    def tag(parser, token):
        class TagNode(template.Node):
            def render(self, context):
                return renderer(context, token)
        return TagNode()
    for copy_attr in ("__dict__", "__doc__", "__name__"):
        setattr(tag, copy_attr, getattr(renderer, copy_attr))
    return register.tag(tag)

@register_render_tag
def admin_reorder(context, token):
    """
    Called in admin/base_site.html template override and applies custom ordering
    of apps/models defined by settings.ADMIN_REORDER
    """
    # sort key function - use index of item in order if exists, otherwise item
    sort = lambda order, item: (order.index(item), "") if item in order else (
        len(order), item)
    if "app_list" in context:
        # sort the app list
        order = OrderedDict(settings.ADMIN_REORDER)
        context["app_list"].sort(key=lambda app: sort(order.keys(),
            app["app_url"].strip("/").split("/")[-1]))
        for i, app in enumerate(context["app_list"]):
            # sort the model list for each app
            app_name = app["app_url"].strip("/").split("/")[-1]
            if not app_name:
                app_name = app["name"].lower()
            model_order = [m.lower() for m in order.get(app_name, [])]
            context["app_list"][i]["models"].sort(key=lambda model:
            sort(model_order, model["admin_url"].strip("/").split("/")[-1]))
    return ""

В settings.py:

ADMIN_REORDER = (
    ('app1', ('App1Model1', 'App1Model2', 'App1Model3')),
    ('app2', ('App2Model1', 'App2Model2')),
)

(вставьте здесь свои собственные имена приложений. Администратор поместит отсутствующие приложения или модели в конец списка, если вы укажете по меньшей мере две модели в каждом приложении.)

В вашей копии base_site.html:

{% extends "admin/base.html" %}
{% load i18n admin_reorder %}

{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}

{% block branding %}
{% admin_reorder %}
<h1 id="site-name">{% trans 'Django administration' %}</h1>
{% endblock %}

{% block nav-global %}{% endblock %}

Ответ 7

Ответ в июне 2018

Этот ответ похож на идею Василя

Я пытался решить подобные проблемы, а потом увидел такой фрагмент.

Я сделал некоторые модификации на основе этого клипа. Код выглядит следующим образом.

# myproject/setting.py
...
# set my ordering list
ADMIN_ORDERING = [
    ('pizza_app', [
        'Pizzas',
        'Toppings'
    ]),
]
# Creating a sort function
def get_app_list(self, request):
    app_dict = self._build_app_dict(request)
    for app_name, object_list in ADMIN_ORDERING:
        app = app_dict[app_name]
        app['models'].sort(key=lambda x: object_list.index(x['object_name']))
        yield app


# Covering django.contrib.admin.AdminSite.get_app_list
from django.contrib import admin

admin.AdminSite.get_app_list = get_app_list
...

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

Отлично работает на Django 2.0

Ответ 8

Также может использоваться небольшой сокращенный код, который называется "Пользовательское приложение Jango Admin Index и модельное упорядочение" . Фрагмент разрешает проблему только с 3-мя простым редактированием.

Вот посмотрите на детали. http://djangosnippets.org/snippets/2613/

Ответ 10

Если вы используете Suit для AdminSite, вы можете выполнить настройку меню с помощью тег меню.

Ответ 11

Я искал простое решение, где я мог бы заказывать приложения по их имени на панели администратора. Я придумал следующий тег шаблона:

from django import template
from django.conf import settings
register = template.Library()

@register.filter
def sort_apps(apps):
    apps.sort(
        key = lambda x:
        settings.APP_ORDER.index(x['app_label'])
        if x['app_label'] in settings.APP_ORDER
        else len(apps)
    )
    print [x['app_label'] for x in apps]
    return apps

Затем просто переопределите templates/admin/index.html и добавьте этот тег шаблона:

{% extends "admin/index.html" %}
{% block content %}
{% load i18n static sort_apps %}
<div id="content-main">

{% if app_list %}
    {% for app in app_list|sort_apps %}
        <div class="app-{{ app.app_label }} module">
        <table>
        <caption>
            <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
        </caption>
        {% for model in app.models %}
            <tr class="model-{{ model.object_name|lower }}">
            {% if model.admin_url %}
                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
            {% else %}
                <th scope="row">{{ model.name }}</th>
            {% endif %}

            {% if model.add_url %}
                <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
            {% else %}
                <td>&nbsp;</td>
            {% endif %}

            {% if model.admin_url %}
                <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
            {% else %}
                <td>&nbsp;</td>
            {% endif %}
            </tr>
        {% endfor %}
        </table>
        </div>
    {% endfor %}
{% else %}
    <p>{% trans "You don't have permission to edit anything." %}</p>
{% endif %}
</div>
{% endblock %}

Затем настройте APP_ORDER в settings.py:

APP_ORDER = [
    'app1',
    'app2',
    # and so on...
]

Он отлично работает на Django 1.10

Ответ 12

Это просто дикий удар в темноте, но есть ли вероятность, что порядок, в котором вы вызываете admin.site.register(< Model class > , < ModelAdmin class > ), может определить порядок отображения? На самом деле, я сомневаюсь, что это сработает, потому что я считаю, что Django поддерживает реестр объектов Model → ModelAdmin, реализованных как стандартный словарь Python, который не поддерживает упорядочение итераций.

Если это не так, как вы хотите, вы всегда можете поиграть с источником в django/contrib/admin. Если вам нужен поддерживаемый порядок итераций, вы можете заменить объект _registry в классе AdminSite (в admin/sites.py) с помощью UserDict или DictMixin, который поддерживает порядок вставки для ключей. (Но, пожалуйста, проконсультируйтесь с этим советом с солью, так как я никогда не делал таких изменений самостоятельно, и я только понимаю, как Django выполняет итерацию по коллекции объектов ModelAdmin. Я думаю, что django/contrib/admin/sites.py - это то место, где нужно искать этот код, а методы AdminSite и register() и index() в частности - это то, что вы хотите.)

Очевидно, что самое приятное здесь было бы простым вариантом для вашего указания в вашем собственном модуле /admin.py. Я уверен, что такой ответ вы надеялись получить. Я не уверен, существуют ли эти варианты.

Ответ 13

Мое решение заключалось в создании подклассов django.contrib.admin.sites.AdminSite и django.contrib.admin.options.ModelAdmin.

Я сделал это, чтобы отобразить более описательное название для каждого приложения и заказать внешний вид моделей в каждом приложении. Таким образом, у меня есть dict в моих settings.py, который сопоставляет app_labels с описательными именами и порядком, в котором они должны появляться, модели упорядочены по порядковому полю, которое я предоставляю в каждом ModelAdmin, когда я регистрирую их с сайтом администратора.

Хотя создание собственных подклассов AdminSite и ModelAdmin рекомендуется в документах, мое решение выглядит как уродливый взлом в конце.

Ответ 14

Вот версия, которая дает вам немного больше гибкости, а именно:

  • Вы можете частично определить порядок приложений, оставляя остальное для Django, чтобы добавить в список
  • Вы можете указать порядок для модулей или избежать его определения, используя вместо этого '*'
  • Сначала будет отображаться порядок определенных вами приложений, а затем добавляются все остальные приложения, добавленные после него.
  • Чтобы проверить имя вашего приложения, посмотрите файл apps.py внутри каталога приложения и проверьте свойство name class Config(AppConfi): или, если его нет, используйте имя каталога для приложения. в проекте.

Добавьте этот код где-нибудь в вашем файле settings.py:

# ======[Setting the order in which the apps/modules show up listed on Admin]========
# set my ordering list
ADMIN_ORDERING = [
    ('crm', '*'),
    ('property', '*'),
]


# Creating a sort function
def get_app_list(self, request):
    """
    Returns a sorted list of all the installed apps that have been
    registered in this site.

    Allows for:
        ADMIN_ORDERING = [
            ('app_1', [
                'module_1',
                'module_2'
            ]),
            ('app_2', '*'),
        ]
    """

    app_dict = self._build_app_dict(request)

    # Let start by sorting the apps alphabetically on a list:
    app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())

    # Sorting the models alphabetically within each app.
    for app in app_list:
        if app['app_label'] in [el[0] for el in ADMIN_ORDERING]:
            app_list.remove(app)
        else:
            app['models'].sort(key=lambda x: x['name'])

    # Now we order the app list in our defined way in ADMIN_ORDERING (which could be a subset of all apps).
    my_ordered_apps = []
    if app_dict:
        for app_name, object_list in ADMIN_ORDERING:
            app = app_dict[app_name]
            if object_list == '*':
                app['models'].sort(key=lambda x: x['name'])
            else:
                app['models'].sort(key=lambda x: object_list.index(x['object_name']))
            my_ordered_apps.append(app)

        # Now we combine and arrange the 2 lists together
        my_ordered_apps.extend(app_list)

    return my_ordered_apps

# Covering django.contrib.admin.AdminSite.get_app_list
from django.contrib import admin
admin.AdminSite.get_app_list = get_app_list
# =========================================

Это всего лишь перезапись функции, определенной Django в файле python2.7/site-packages/django/contrib/admin/sites.py.

Этот метод get_app_list class AdminSite(object): создает структуру данных со всеми приложениями в проекте, в том числе для приложения аутентификации Django, например:

[
  {
    "app_label": "auth",
    "app_url": "/admin/auth/",
    "has_module_perms": "True",
    "models": [
      {
        "add_url": "/admin/auth/group/add/",
        "admin_url": "/admin/auth/group/",
        "name": "<django.utils.functional.__proxy__ object at 0x11057f990>",
        "object_name": "Group",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      },
      {
        "add_url": "/admin/auth/user/add/",
        "admin_url": "/admin/auth/user/",
        "name": "<django.utils.functional.__proxy__ object at 0x11057f710>",
        "object_name": "User",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      }
    ],
    "name": "<django.utils.functional.__proxy__ object at 0x108b81850>"
  },
  {
    "app_label": "reservations",
    "app_url": "/admin/reservations/",
    "has_module_perms": "True",
    "models": [
      {
        "add_url": "/admin/reservations/reservationrule/add/",
        "admin_url": "/admin/reservations/reservationrule/",
        "name": "<django.utils.functional.__proxy__ object at 0x11057f6d0>",
        "object_name": "ReservationRule",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      }
    ],
    "name": "Availability"
  },
  {
    "app_label": "blog",
    "app_url": "/admin/blog/",
    "has_module_perms": "True",
    "models": [
      {
        "add_url": "/admin/blog/category/add/",
        "admin_url": "/admin/blog/category/",
        "name": "Categories",
        "object_name": "Category",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      },
      {
        "add_url": "/admin/blog/post/add/",
        "admin_url": "/admin/blog/post/",
        "name": "<django.utils.functional.__proxy__ object at 0x11057f110>",
        "object_name": "Post",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      },
      {
        "add_url": "/admin/blog/tag/add/",
        "admin_url": "/admin/blog/tag/",
        "name": "<django.utils.functional.__proxy__ object at 0x11057f390>",
        "object_name": "Tag",
        "perms": {
          "add": "True",
          "change": "True",
          "delete": "True"
        }
      }
    ],
    "name": "Blog"
  },
(...)
]

Ответ 15

Скопируйте lib\site-packages\django\contrib\admin\templates\admin\index.html template в каталог project1\templates\admin\, где project1 - это имя вашего проекта.

В скопированном файле, например, project1\templates\admin\index.html, замените строки:

{% block content %} 
...
{% endblock %}

с:

{% block content %}
<div id="content-main">

{% if app_list %}
    <div class="module">
        <table>
            <caption>App 1</caption>
            <tr>  <th>  <a href="/admin/app1/model1/">Model 1</a>  </th>  <td>Description of model 1</td>  </tr>
            <tr>  <th>  <a href="/admin/app1/model2/">Model 2</a>  </th>  <td>Description of model 1</td>  </tr>
            <tr>  <th>  <a href="..."                >...</a>      </th>  <td>...</td>                     </tr>
        </table>
    </div>

    <div class="module">
        <table>
            <caption>Authentication and authorization</caption>
            <tr>  <th>  <a href="/admin/auth/user/"  >Users</a>    </th>  <td>List of users</td>           </tr>
            <tr>  <th>  <a href="/admin/auth/group/" >Groups</a>   </th>  <td>List of users' groups</td>   </tr>
        </table>
    </div>
{% else %}
    <p>{% trans "You don't have permission to view or edit anything." %}</p>
{% endif %}

</div>
{% endblock %}  

где:

  • app1 - это название вашего приложения с моделями,
  • modeli - это название i-й модели в app1.

Если вы определили более одного приложения с моделями в своем проекте, просто добавьте другую таблицу в приведенный выше файл index.html.

Поскольку мы меняем шаблон, мы можем свободно изменять его HTML-код. Например, мы можем добавить описание моделей, как было показано выше. Вы также можете восстановить ссылки "Добавить" и "Изменить" - я удалил их, так как считаю их избыточными.

Ответ - практическая демонстрация решения от ответа Дэйва Каспера.