订购admin.ModelAdmin对象

时间:2008-12-29 17:25:50

标签: django django-admin

假设我的披萨应用程序有Topping和Pizza课程,他们会在Django Admin中显示:

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

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

但我希望他们这样:

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

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

如何在admin.py中配置它?

17 个答案:

答案 0 :(得分:29)

您可以尝试的解决方法是调整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:对不起,如果这个答案发布得太晚,但在未来的类似情况下可以帮助其他人。

答案 1 :(得分:18)

如果你想在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

当然,它不是优雅的,但在我们得到更好的解决方案之前可能会工作一段时间。

答案 2 :(得分:18)

由于这个Django snippet,我最终设法做到了,您只需要了解ADMIN_REORDER设置:

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

app1不得以项目名称为前缀,即使用app1代替mysite.app1

答案 3 :(得分:15)

现在有一个很好的Django包:

https://pypi.python.org/pypi/django-modeladmin-reorder

答案 4 :(得分:10)

这实际上涵盖在Part 2 of the Django Tutorial的最底层。

以下是相关部分:

  

自定义管理员索引页

     

在类似的说明中,您可能想要   定制的外观和感觉   Django管理员索引页。

     

默认情况下,它会显示所有应用   在已安装的INSTALLED_APPS中   在管理员申请中注册,   按字母顺序排列。你可能想要   做出重大改变   布局。毕竟,索引是   可能是最重要的一页   管理员,应该很容易   使用

     

要自定义的模板是   管理员/ index.html的。 (与之相同   以前的admin / base_site.html   部分 - 从默认值中复制它   目录到您的自定义模板   目录。)编辑文件,你就可以了   看到它使用一个名为的模板变量   app_list。该变量包含每个   安装了Django应用程序。而不是使用   那,你可以硬链接到链接   特定于对象的管理页面   你认为最好的方式。

答案 5 :(得分: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 %}

答案 6 :(得分:3)

2018年6月答复

此答案类似于Vasil's idea

我试图解决类似的问题,然后看到了这样的the fragment

我根据此剪辑进行了一些修改。代码如下。

# 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上效果很好

答案 7 :(得分:2)

我把它添加到Django的trac:

http://code.djangoproject.com/ticket/9928

答案 8 :(得分:2)

如果您使用Suit作为AdminSite,则可以使用menu tag进行菜单自定义。

答案 9 :(得分:0)

我的解决方案是制作django.contrib.admin.sites.AdminSite和django.contrib.admin.options.ModelAdmin的子类。

我这样做了所以我可以为每个应用显示更具描述性的标题,并在每个应用中订购模型的外观。所以我在settings.py中有一个dict,它将app_labels映射到描述性名称和它们应该出现的顺序,模型按照我在每个ModelAdmin中提供的序数字段进行排序,当我向管理站点注册时。

虽然在文档中鼓励创建自己的AdminSite和ModelAdmin子类,但我的解决方案最终看起来像是一个丑陋的黑客。

答案 10 :(得分:0)

这只是一个黑暗中的疯狂刺,但你有可能调用admin.site.register(&lt; Model class&gt;,&lt; ModelAdmin class&gt;)的顺序可以确定显示顺序?实际上,我怀疑这会有效,因为我相信Django维护着一个模型的注册表 - &gt; ModelAdmin对象实现为标准Python字典,不保持迭代排序。

如果这不符合您的要求,您可以随时使用django / contrib / admin中的源代码。如果需要维护迭代顺序,可以将AdminSite类(在admin / sites.py中)中的_registry对象替换为维护键的插入顺序的UserDict或DictMixin。 (但是请大家接受这个建议,因为我自己从未做过这些改变,而且我只是在猜测Django如何迭代ModelAdmin对象的集合。我认为django / contrib /admin/sites.py是查找此代码的地方,特别是AdminSite类和register()以及index()方法就是你想要的。)

显然,最好的方法是在您自己的/admin.py模块中指定一个简单的选项。我确信这是你希望得到的那种答案。不过,我不确定这些选项是否存在。

答案 11 :(得分:0)

这是一个为您提供更多灵活性的版本,即:

  • 您可以部分定义应用程序的排序,其余部分供Django添加到列表中
  • 您可以指定模块的顺序,或者避免使用'*'来定义顺序。
  • 您定义的应用顺序将首先显示,然后所有其余的应用附加在其后
  • 要检查应用程序的名称,请查看应用程序目录中的文件apps.py并检查class Config(AppConfi):的name属性,或者在不存在的情况下,使用项目中应用程序的目录。

将此代码添加到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的{​​{1}}方法会生成项目上所有应用程序的数据结构,包括Django的auth应用程序,例如:

class AdminSite(object):

答案 12 :(得分:0)

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是带有模型的应用程序的名称,
  • modeliapp1中第i个模型的名称。

如果您在项目中使用模型定义了多个应用程序,则只需在上述index.html文件中添加另一个表即可。

由于我们更改了模板,因此可以自由更改其HTML代码。例如,我们可以添加上面显示的模型描述。您还可以恢复 Add Change 链接-我删除了它们,因为我认为它们是多余的。

答案是Dave Kasper's answer中解决方案的实践证明。

答案 13 :(得分:0)

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),
]

答案 14 :(得分:0)

将以下代码添加到您的 settings.py:

def get_app_list(self, request):
    """
    Return a sorted list of all the installed apps that have been
    registered on this site.
    """
    ordering = {
        # for superuser
        'Group': 1,
        'User': 2,

        # fist app
        'TopMessage': 101,
        'Slider': 102,
        'ProductCategory': 103,
        'Product': 104,
        'ProductPicture': 105,
        
        # 2nd app
        'ProductReview': 201,
        'Promotion': 202,
        'BestSeller': 203,
    }
    app_dict = self._build_app_dict(request)
    app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())

    for app in app_list:
        app['models'].sort(key=lambda x: ordering[x['object_name']])

    return app_list


admin.AdminSite.get_app_list = get_app_list

并更改排序字典以匹配您的应用和模型。就是这样。

我的解决方案的好处是,如果用户是超级用户,它将显示“身份验证”模型。

答案 15 :(得分:0)

默认情况下,Django 按字母顺序对 admin 中的模型进行排序。所以Event admin中模型的顺序是Epic, EventHero, EventVillain, Event

相反,您希望订单是

  • EventHero、EventVillain、Epic 然后是事件。

用于呈现管理索引页面的模板是 admin/index.html,视图函数是 ModelAdmin.index

def index(self, request, extra_context=None):
"""
Display the main admin index page, which lists all of the installed
apps that have been registered in this site.
"""
app_list = self.get_app_list(request)
context = {
    **self.each_context(request),
    'title': self.index_title,
    'app_list': app_list,
    **(extra_context or {}),
}

request.current_app = self.name

return TemplateResponse(request, self.index_template or
    'admin/index.html', context)

方法get_app_list,设置模型的顺序。:

def get_app_list(self, request):
"""
Return a sorted list of all the installed apps that have been
registered in this site.
"""
app_dict = self._build_app_dict(request)

# Sort the apps alphabetically.
app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())

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

return app_list

所以要设置我们覆盖 get_app_list 的顺序:

class EventAdminSite(AdminSite):
def get_app_list(self, request):
    """
    Return a sorted list of all the installed apps that have been
    registered in this site.
    """
    ordering = {
        "Event heros": 1,
        "Event villains": 2,
        "Epics": 3,
        "Events": 4
    }
    app_dict = self._build_app_dict(request)
    # a.sort(key=lambda x: b.index(x[0]))
    # Sort the apps alphabetically.
    app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())

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

    return app_list

代码 app['models'].sort(key=lambda x: ordering[x['name']]) 设置固定顺序。您的应用现在看起来像这样。

enter image description here

Check the Documentation

答案 16 :(得分:0)

Settings.py 文件中设置应用程序/模块在管理中列出的显示顺序

设置我的订购清单

ADMIN_ORDERING = [
    ('Your_App1', '*'),
    ('Your_App2', '*'),
]

在 ADMIN_ORDERING 下面创建一个排序函数

def get_app_list(self, request):
    """
    You Can Set Manually Ordering For Your Apps And Models   

        ADMIN_ORDERING = [
            ('Your_App1', [
                'module_1',
                'module_2'
            ]),
            ('Your_App2', '*'),
        ]
    """

    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