我该如何为Django管理后台创建自定义页面?

52

我希望在没有模型的情况下为管理员面板创建自定义页面。首先,我将index.html复制到项目文件夹中:

mysite/
    templates/
        admin/
            index.html

然后将我的代码添加到应用程序块中:

<div class="module">
    <table summary="{% blocktrans with name="preferences" %}Models available in the preferences application.{% endblocktrans %}">
        <caption><a href="preferences" class="section">{% blocktrans with name="preferences" %}Preferences{% endblocktrans %}</a></caption>
            <tr>
                <th scope="row"><a href="preferences">Preferences</a></th>
                <td><a href="preferences" class="changelink">{% trans 'Change' %}</a></td>
            </tr>
    </table>
</div>

这样做很有效果,然后我创建了新的页面/templates/admin/preferences/preferences.html,并且在urls.py中添加了:

url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),

并在 preferences.html 中添加代码:

{% extends "admin/base_site.html" %}
{% block title %}Test page{% endblock %}

运行代码后,提示出现错误消息"The requested admin page does not exist."。我做错了什么?


你把URL添加到了哪个urls.py文件中?可能是因为Django管理后台中的一个更通用的正则表达式捕获了/admin/preferences,所以它从未到达你的URL正则表达式。 - Simeon Visser
我在 /mysite/mysite/urls.py 中只有一个 urls.py 文件,我认为这不是 urls 的错误,因为我的 urls 模式没有任何错误。 - Gr1N
如果是这种情况,您将不会收到错误消息。您是否尝试更改URL为其他内容,以查看在这种情况下是否可以获取管理页面?例如,^ testadmin / preferences / $ - Simeon Visser
使用此URL很好。我可以看到我的首选项页面,但我想使用^admin/preferences/$ URL。 - Gr1N
8个回答

36

在管理界面的URL模式之前,您需要添加管理员URL:

urlpatterns = patterns('',
   url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
   url(r'^admin/', include('django.contrib.admin.urls')),
)
这样URL就不会被Django的管理系统处理。

5
这个页面会被保护还是对公众开放? - Dwayne Crooks
1
@DwayneCrooks 我晚了几年,但是它需要通过使用 python manage.py createsuperuser 创建的超级用户登录。 - ICanKindOfCode
5
不,它没有。 - Adam

27

岁月流转,对此问题的一个相关回答仍然可以发布。

使用Django 1.10+,您可以执行以下操作:

security/admin.py(这是您的应用程序管理文件)

from django.contrib import admin
from django.conf.urls import url
from django.template.response import TemplateResponse
from security.models import Security


@admin.register(Security)
class SecurityAdmin(admin.ModelAdmin):

    def get_urls(self):

        # get the default urls
        urls = super(SecurityAdmin, self).get_urls()

        # define security urls
        security_urls = [
            url(r'^configuration/$', self.admin_site.admin_view(self.security_configuration))
            # Add here more urls if you want following same logic
        ]

        # Make sure here you place your added urls first than the admin default urls
        return security_urls + urls

    # Your view definition fn
    def security_configuration(self, request):
        context = dict(
            self.admin_site.each_context(request), # Include common variables for rendering the admin template.
            something="test",
        )
        return TemplateResponse(request, "configuration.html", context)

安全/templates/configuration.html

{% extends "admin/base_site.html" %}
{% block content %}
...
{% endblock %}

请参考官方 ModelAdmin.get_urls 说明文档(请确保您选择了正确的 Django 版本,此代码适用于1.10版本及以上)。


它在没有安全性的情况下工作。在导入时出现错误:ModuleNotFoundError: No module named 'security'。 - Rob Michiels
有人能解释一下为什么这个解决方案同时使用了装饰器和继承ModelAdmin吗? - kta

13

你应该使用管理员的get_urls方法。


15
get_urlsModelAdmin 的一个方法,它需要一个 Model 对象作为参数。但是,OP 特别想要 "在不使用模型的情况下创建自定义管理面板页面"。(强调部分已添加) - Louis
1
他的意思是AdminSite的get_urls函数。 - pondermatic
是的,请看这里:https://docs.djangoproject.com/en/dev/ref/contrib/admin/#adding-views-to-admin-sites - Mitar

6
如果您想创建一个自定义页面,只需在那里放置任意表单以处理用户输入,您可以尝试使用 django-etc。有一个etc.admin.CustomModelPage可供使用:
    # admin.py
    from etc.admin import CustomModelPage

    class MyPage(CustomModelPage):
    
        title = 'My custom page'  # set page title

        # Define some fields you want to proccess data from.
        my_field = models.CharField('some title', max_length=10)

        def save(self):
            # Here implement data handling.
            super().save()

    # Register the page within Django admin.
    MyPage.register()

5

2
此问题的关键是 _管理员面板 不包括 模型_,而此解决方案与模型相关联。 - Manu Artero

5

完整例子:

from django.urls import path
from django.contrib import admin
from django.db import models

class DummyModel(models.Model):
    class Meta:
        verbose_name = 'Link to my shiny custom view'
        app_label = 'users'  # or another app to put your custom view

@admin.register(DummyModel)
class DummyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        view_name = '{}_{}_changelist'.format(
                DummyModel._meta.app_label, DummyModel._meta.model_name)
        return [
            path('my_view/', MyCustomView.as_view(), name=view_name)
        ]

采用这种方法,Django的makemigrations命令将创建数据库迁移,以创建DummyModel的表。


@decadenza,你的应用程序中是否在INSTALLED_APPS中列出了虚拟模型? - Mark Mishyn
是的,我仍在努力理解问题所在。谢谢。 - decadenza
尝试重新启动服务器。 - avalanchy
Django会在数据库中查找users_dummy_model。 - Thanatos11th
非常简单,有点在盒子里思考,同时也在盒子外思考。好主意。 - Harlin

2
扩展AdminSite类对我来说效果最好,如django文档所述。这个解决方案通过管理站点登录机制保护页面,并且设置起来比看上去容易:
  1. 在其他管理员代码(例如myapp/admin.py)中,扩展默认类:
from django.contrib.admin import AdminSite

class CustomAdminSite(AdminSite):

    def get_urls(self):
        custom_urls = [
            path('admin/preferences/', self.admin_view(views.my_view)),
        ]
        admin_urls = super().get_urls()
        return custom_urls + admin_urls  # custom urls must be at the beginning


site = CustomAdminSite()

# you can register your models on this site object as usual, if needed
site.register(Model, ModelAdmin)

实现视图。
def my_view(request):
    return render(request, 'admin/preferences/preferences.html')

使用urls.py中的管理员站点,而不是默认的站点。
from myapp import admin

# now use admin.site as you would use the default django one
urlpatterns = [
    # ...
    path('admin/', admin.site.urls),
    # ...
]

0
如果您想将页面挂钩到现有的管理站点中,可以按照以下步骤操作,这是基于#arnaud-p的上面的答案。Arnaud的答案对我不起作用,因为子类化adminsite的get_url函数会失去对现有管理页面的访问权限,直到我添加了注册表如下所示。 使用以下方法,您的附加页面将需要员工访问权限,而且您无需更改urls.py,因此非常适合为应用程序等创建管理页面...您可以在视图中传递each_context以获取权限等。 适用于django 3.2.9
在admin.py中:
from django.contrib import admin
from django.urls import path

from . import views

class CustomAdminSite(admin.AdminSite):
    
    def get_urls(self):
        self._registry = admin.site._registry
        admin_urls = super().get_urls() 
        custom_urls = [
            path('preferences/', views.Preferences.as_view(admin=self), name="preferences"),
        ]
        return custom_urls + admin_urls # custom urls must be at the beginning

    def get(self):
        request.current_app == self.name
        return super().get(request)

    def get_app_list(self, request):
        app_list = super().get_app_list(request)
        app_list += [
            {
                "name": "My Custom Preferences App",
                "app_label": "Preferences",
                # "app_url": "/admin/test_view",
                "models": [
                    {
                        "name": "Preferences",
                        "object_name": "preferences",
                        "admin_url": "/admin/preferences",
                        "view_only": True,
                    }
                ],
            }
        ]
        return app_list

site = CustomAdminSite()

视图...

class Preferences(views.generic.ListView):
    admin = {}
    def get(self, request):
        ctx = self.admin.each_context(request)
        return render(request, 'admin/preferences/preferences.html', ctx)

模板...

{% extends "admin/base_site.html" %}
{% block content %}
...HELLO WORLD!
{% endblock %}

我编辑了这个内容,因为我的原始代码中admin_urls = admin.site.urls丢失了来自超类的get_app_list覆盖,所以它从未被调用。为了使get_app_list正常工作并获得已注册的普通应用程序模型,我必须将customadmin._registry设置为admin.site._registry。 - miller the gorilla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接