Заказ объектов admin.ModelAdmin
Скажем, у меня есть приложение для пиццы с классами Topping и Pizza, и они отображаются в Django Admin следующим образом:
PizzaApp
-
Toppings >>>>>>>>>> Add / Change
Pizzas >>>>>>>>>> Add / Change
Но я хочу, чтобы они были такими:
PizzaApp
-
Pizzas >>>>>>>>>> Add / Change
Toppings >>>>>>>>>> Add / Change
Как мне настроить это в моей admin.py?
Ответы
Ответ 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
.
Ответ 5
Теперь для этого есть приятный пакет Django:
https://pypi.python.org/pypi/django-modeladmin-reorder
Ответ 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/
Ответ 9
Я добавил его в Django trac:
http://code.djangoproject.com/ticket/9928
Ответ 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> </td>
{% endif %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td> </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-код. Например, мы можем добавить описание моделей, как было показано выше. Вы также можете восстановить ссылки "Добавить" и "Изменить" - я удалил их, так как считаю их избыточными.
Ответ - практическая демонстрация решения от ответа Дэйва Каспера.