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 路由表
Django中自定义AdminSite
最新推荐文章于 2025-03-25 10:00:00 发布