Django中自定义AdminSite

from django.contrib import admin  # 导入 Django 的 admin 模块
from functools import update_wrapper  # 导入 update_wrapper,用于包装视图函数
from django.views.generic import RedirectView  # 导入 RedirectView 用于重定向
from django.urls import reverse  # 导入 reverse 以便在代码中反向解析 URL
from django.views.decorators.cache import never_cache  # 导入 never_cache 以禁用缓存
from django.views.decorators.csrf import csrf_protect  # 导入 csrf_protect 以启用 CSRF 保护
from django.http import HttpResponseRedirect  # 导入 HttpResponseRedirect 进行页面跳转
from django.urls import include, path, re_path  # 导入 URL 相关模块
from django.contrib.contenttypes import views as contenttype_views  # 导入 contenttypes 视图
from django.contrib.auth.views import redirect_to_login  # 导入 redirect_to_login 进行登录重定向

# 自定义管理站点类,继承自 Django 的 AdminSite
class MyAdminSite(admin.AdminSite):
    # 自定义 admin 视图,增加权限校验和 CSRF 保护
    def admin_view(self, view, cacheable=False):
        def inner(request, *args, **kwargs):
            # 如果用户没有访问权限
            if not self.has_permission(request):
                # 如果当前路径是注销页面,则重定向到后台首页
                if request.path == reverse('admin:logout', current_app=self.name):
                    index_path = reverse('admin:index', current_app=self.name)
                    return HttpResponseRedirect(index_path)
                # 否则,跳转到自定义的登录页面
                return redirect_to_login(
                    request.get_full_path(),  # 获取当前完整路径
                    '/user/login.html'  # 自定义登录页面 URL
                )
            return view(request, *args, **kwargs)  # 如果有权限,继续执行视图
        
        if not cacheable:
            inner = never_cache(inner)  # 如果不允许缓存,则应用 never_cache 装饰器
        
        if not getattr(view, 'csrf_exempt', False):
            inner = csrf_protect(inner)  # 如果视图未免除 CSRF 保护,则应用 csrf_protect 装饰器
        
        return update_wrapper(inner, view)  # 更新 wrapper 以保持原函数属性

    # 重写 get_urls 方法,自定义 admin 站点的 URL 配置
    def get_urls(self):
        # 包装视图函数,使其通过 admin_view 进行权限和 CSRF 保护
        def wrap(view, cacheable=False):
            def wrapper(*args, **kwargs):
                return self.admin_view(view, cacheable)(*args, **kwargs)
            wrapper.admin_site = self  # 绑定 admin 站点实例
            return update_wrapper(wrapper, view)  # 更新 wrapper 以保持原函数属性
        
        # 定义后台 URL 路由
        urlpatterns = [
            path('', wrap(self.index), name='index'),  # 后台首页
            path('login/', RedirectView.as_view(url='/user/login.html')),  # 自定义登录页面的重定向
            path('logout/', wrap(self.logout), name='logout'),  # 注销页面
            path('password_change/', wrap(self.password_change, cacheable=True), name='password_change'),  # 修改密码页面
            path(
                'password_change/done/',
                wrap(self.password_change_done, cacheable=True),
                name='password_change_done',  # 修改密码成功页面
            ),
            path('jsi18n/', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'),  # JavaScript 国际化支持
            path(
                'r/<int:content_type_id>/<path:object_id>/',
                wrap(contenttype_views.shortcut),
                name='view_on_site',  # 站点对象快捷访问
            ),
        ]
        
        valid_app_labels = []  # 记录已注册的应用标签
        
        # 遍历已注册的模型,为每个模型添加 URL 规则
        for model, model_admin in self._registry.items():
            urlpatterns += [
                path('%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
            ]
            # 记录当前应用标签,避免重复
            if model._meta.app_label not in valid_app_labels:
                valid_app_labels.append(model._meta.app_label)
        
        # 如果存在已注册的应用,则为其生成应用索引页面的 URL
        if valid_app_labels:
            regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$'  # 构造正则匹配规则
            urlpatterns += [
                re_path(regex, wrap(self.app_index), name='app_list'),  # 绑定应用列表页面
            ]
        
        return urlpatterns  # 返回完整的 URL 路由表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值