Скажем, у меня есть приложение для пиццы с классами Topping и Pizza, и они отображаются в Django Admin следующим образом:
PizzaApp
-
Toppings >>>>>>>>>> Add / Change
Pizzas >>>>>>>>>> Add / Change
Но я хочу их так:
PizzaApp
-
Pizzas >>>>>>>>>> Add / Change
Toppings >>>>>>>>>> Add / Change
Как мне настроить это в моем admin.py?





Это просто дикий удар в темноте, но есть ли шанс, что порядок, в котором вы вызываете admin.site.register (<класс модели>, <класс ModelAdmin>), может определять порядок отображения? На самом деле, я сомневаюсь, что это сработает, потому что я считаю, что 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. Я уверен, что вы надеялись получить именно такой ответ. Однако я не уверен, существуют ли такие варианты.
Я добавил это в трек Django:
http://code.djangoproject.com/ticket/9928
Это еще не сделано!
На самом деле это описано в самом низу Написание вашего первого приложения Django, часть 7.
Вот соответствующий раздел:
Customize the admin index page
On a similar note, you might want to customize the look and feel of the Django admin index page.
By default, it displays all the apps in INSTALLED_APPS that have been registered with the admin application, in alphabetical order. You may want to make significant changes to the layout. After all, the index is probably the most important page of the admin, and it should be easy to use.
The template to customize is admin/index.html. (Do the same as with admin/base_site.html in the previous section -- copy it from the default directory to your custom template directory.) Edit the file, and you'll see it uses a template variable called app_list. That variable contains every installed Django app. Instead of using that, you can hard-code links to object-specific admin pages in whatever way you think is best.
Это полезно знать, но я думаю, что вопрос касается пары классов Model в одном и том же установленном приложении. Изменение порядка приложений не поможет вам, если вы захотите изменить порядок классов модели в одном приложении.
Хорошая мысль. Они должны быть где-то перечислены, и, возможно, мне просто нужно применить там фильтр сортировки. Я собираюсь посмотреть на это завтра (здесь европейский часовой пояс ...) Спасибо
Я думаю, что стоит попробовать простое решение из 林伟雄 ниже. Только небольшое дополнение в settings.py.
Мое решение заключалось в создании подклассов django.contrib.admin.sites.AdminSite и django.contrib.admin.options.ModelAdmin.
Я сделал это, чтобы отображать более описательные заголовки для каждого приложения и упорядочивать внешний вид моделей в каждом приложении. Итак, у меня есть dict в моем settings.py, который сопоставляет app_labels с описательными именами и порядком, в котором они должны появляться, модели упорядочены по порядковому полю, которое я предоставляю в каждом ModelAdmin, когда я регистрирую их на сайте администратора.
Хотя в документации поощряется создание собственных подклассов AdminSite и ModelAdmin, мое решение в конечном итоге выглядит уродливым хаком.
Обходной путь, который вы можете попробовать, - это настроить свой файл models.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 записи пиццы, он наконец скажет «3 1. Пицца».
Если вы хотите решить эту проблему за 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
Конечно, это не изящно, но может работать некоторое время, прежде чем мы получим лучшее решение.
Работает в последней версии Django 1.11. Обратите внимание, что пробелы в начале не отображаются браузером, поэтому это не оказывает визуального воздействия на заголовок. Также обратите внимание, что это работает с классами AppConfig, чтобы также изменить порядок разделов приложений.
Супер резкий, но определенно самый быстрый и грязный!
Также можно использовать небольшой фрагмент кода, который называется 'Заказ пользовательских приложений и моделей в Django Admin Index'. Фрагмент решает проблему всего за 3 простых редактирования.
Вот подробности. http://djangosnippets.org/snippets/2613/
В конце концов мне удалось это сделать благодаря этому Фрагмент Django, вам просто нужно знать настройку ADMIN_REORDER:
ADMIN_REORDER = (
('app1', ('App1Model1', 'App1Model2', 'App1Model3')),
('app2', ('App2Model1', 'App2Model2')),
)
app1 не должен начинаться с префикса имени проекта, т.е. используйте app1 вместо mysite.app1.
Что ж, это сработало в версии 1.3, с какой проблемой вы столкнулись?
Что может быть неясным, так это то, что вам нужен весь фрагмент, а не только блок кода, опубликованный в этом ответе.
Вдохновленный фрагментом, я написал github.com/mishbahr/django-modeladmin-reorder
@mishbah, не могли бы вы обновить его для Django 2.x или какой-либо справочной ссылки о том, как это сделать для внутренних страниц?
Вот фрагмент, который использовал Эммануэль, обновленный для 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 %}
Для этого есть хороший пакет Django:
Я искал простое решение, в котором можно было бы упорядочивать приложения по их имени в панели администратора. Я придумал следующий тег шаблона:
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.
Ага, отличная работа в Django 2.0. Для инициализации вы можете использовать ключ libraries в TEMPLATES['OPTIONS'] dict следующим образом: `` 'библиотеки': {'sort_apps': 'src.apps.utils.templatetags.customize_tags',} `` Без добавления в INSTALLED_APPS
@ teror4uks не могли бы вы объяснить это еще немного?
Ответ в июне 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
Если есть план по оптимизации этого кода или других реализаций, пожалуйста, поделитесь им со мной. Спасибо!
Это вернуло ValueError: слишком много значений для распаковки (ожидалось 2), когда я пытался это сделать. Django 2.1 здесь.
Я не знаю, откуда произошла ошибка, не могли бы вы показать мне свой стек исключений? @Nathan
Я написал свой собственный сценарий, чтобы позаботиться об этом. Я писал об этом здесь: zhuzhu-limited.com/django-admin-app-and-model-reordering
Это самое простое и элегантное решение! Отличная работа.
Это здорово. Прекрасно работает на Django 2.2 с администратором Django по умолчанию. Все изменения есть в settings.py, не нужно редактировать шаблоны, что требуется в большинстве других решений.
Большой ! Работает с 3.1. Я добавил следующее после вашей команды yield: explicit_app_names = [app_name for app_name, object_list in ADMIN_ORDERING] for app_name in app_dict: if app_name not in explicit_app_names: yield app_dict[app_name] Итак, у меня есть все в админке, и я могу изменить порядок только тех приложений, которые важны для меня. Такие явно определенные приложения тогда оказываются на вершине, но я думаю, что такое поведение неплохо.
Вот версия, которая дает вам немного больше гибкости, а именно:
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's 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"
},
(...)
]
Скопируйте шаблон lib\site-packages\django\contrib\admin\templates\admin\index.html в каталог 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-код. Например, мы можем добавить описание моделей, как было показано выше. Вы также можете восстановить ссылки Добавлять и Изменять - я удалил их, так как считаю их избыточными.
Ответ - практическая демонстрация решения от Ответ Дэйва Каспера.
custom_admin.py
from django.contrib.admin import AdminSite
class CustomAdminSite(AdminSite):
def get_urls(self):
urls = super(MyAdminSite, self).get_urls()
return urls
def get_app_list(self, request):
app_dict = self._build_app_dict(request)
ordered_app_list = []
if app_dict:
# TODO: change this dict
admin_ordering = {
'app1': ('Model1', 'Model2'),
'app2': ('Model7', 'Model4'),
'app3': ('Model3',),
}
ordered_app_list = []
for app_key in admin_ordering:
app = app_dict[app_key]
app_ordered_models = []
for model_name in admin_ordering[app_key]:
for model in app_dict[app_key]['models']:
if model['object_name'] == model_name:
app_ordered_models.append(model)
break
app['models'] = app_ordered_models
ordered_app_list.append(app)
return ordered_app_list
admin_site = CustomAdminSite ()
urls.py
from custom_admin import admin_site
urlpatterns = [
path('admin/', admin_site.urls),
]
Да, я надеялся получить такой ответ и боялся такого. Я думаю, что вы правы, по крайней мере, из того, что я нашел в документации. Думаю, я оставлю это как есть. Я не хочу поддерживать свою собственную версию django.