跳转至

Admin站点

add_circle2025-03-05update2025-03-06

django内置了一个强大的组件叫Admin,提供给网站管理员快速开发运营后台的管理站点。

提醒:虽然django内置的运营站点功能齐全,但是在实际工作中如果要实现高定制性后台运营站点,很多公司都是自己另行自己从0开始搭建的或使用第三方组件对Admin进行增强美化。

站点文档: https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/admin/

注意:要使用Admin,必须先创建超级管理员。
python manage.py createsuperuser

访问地址:http://127.0.0.1:8000/admin,访问效果如下:

1585638280577

admin站点默认并没有提供其他的操作给我们,所以一切功能都需要我们进行配置,在项目中,我们每次创建子应用的时候都会存在一个admin.py文件,这个文件就是用于配置admin站点功能的文件。这些admin.py最终都会被项目运营的时候被django所加载并识别。

一般而言,我们在开发中经常以下几个术语:
1. 前台站点
   一个独立站点,这个站点和后台站点共用了一个数据库,甚至在一个目录下,大家一起运行。
   前台站点一般是允许任何人进行注册,登录并访问。我们在网络中经常访问的网站基本都是各种网站的前台站点。
   例如:京东、淘宝、

2. 后台站点
   也是一个独立站点,这个站点和前台站点共用了一个数据库,甚至在一个目录下,大家一起运行。
   后台一般是提供公司内部不懂代码的工作人员,方便这些人更好的维护或对站点已有的数据库中的内容进行增删查改用的

在绝大部分情况下,我们所说的一个项目,往往都会涵盖了前台站点和后台站点,除了OA(在线办公系统),CRM(客户关系管理系统),ERP(企业资源管理系统)类似这种内部企业项目以外。
不管是前台站点或者后台站点,都是由前端代码(html,css,js)和后端代码(python,java,c++,go,php,.net,C#)所实现的。

3. 前端开发
   指代就是客户端开发: web前端,移动端开发,游戏客户端开发
   web前端: 使用基于http获取服务端数据提供给客户端展示的这一类的开发。目前来说,绝大部分指代的就是js,....
   移动端开发:js,andorid,IOS。。
   游戏客户端:C++,js,java,。。

4. 后端开发[服务端开发]
   python,java,c++,go,php,.net,C#,rust

django的admin站点在没有经过任何配置的之前,就已经实现了目前业内来说比较完善的用户认证机制和权限分配系统了。

用户认证机制:基于django.contrib.auth子应用对外提供的。里面有内置的视图,模板,模型等等。

​ 已经实现了关于管理员的登录,退出,修改密码等等。

权限认证系统:基于django.contrib.auth子应用对外提供的。里面基于模型完成了基于RBAC的权限认证机制.

1. RBAC权限认证机制

RBAC(Role-Base Access Control 的缩写),译作:基于角色分配的访问控制机制。

实现了权限认证机制以后,我们可以让不同的用户得到不同的权限,基于用户拥有的权限不同,能操作的功能或者能看到的站点内容也会产生不一样。

在开发中,我们一般用于项目权限的分配机制无非3种:RBAC,OAuth授权认证、RLS(Row Level Security的缩写、译作:行级数据安全)。

在网站后台运营站点这种单个站点内部,单个站点集群场景下,一般使用的都是RBAC。

在对外开发的业务站点中,基于不同的渠道,不同的领域,不同站点之间,一般都是使用OAuth2.0授权认证。

在对外开发的站点服务,如果配置多台前后台的租赁模式,多数使用RLS权限机制。低代码平台/设计平台/AI平台/租户平台

在实现过程中,因为项目业务的复杂程度不一致。所以存在有3表RBAC或5表RBAC的设计方案。

django的admin站点实际上就是基于5表RBAC的实现方案扩展出来的6表RBAC认证机制

1) 3表RBAC认证

顾名思义,就是使用了3张表保存了权限相关的所有数据,这3张表分别是用户表(manager/user/member),角色表(role/group/department)权限表(permission/auth)

image-20210622092906059

常见的场景:中小型项目,例如:小论坛,小商城,普通的后台站点。

2) 5表RBAC认证

顾名思义,就是在3表的基础上新增了2张关系表保存权限相关的所有数据,这5张表分别是用户表(user),角色表(role/group),权限表(permission/auth),以及新增的2张关系表:用户与角色之间的关系表(usergroup)和角色与权限的关系表(grouppermission)。

image-20211025092728749

常见的业务场景:大型综合论坛,大型的商城(商家入驻),有分公司的企业内部站点,OA、CRM、ERP等管理系统。

3) Django的RBAC

是在传统的5表RBAC基础上,增加了一个关系表,用户与权限之间的关系表(user_permission)。

因为RBAC的本质是用户随着角色不同,而拥有不同的权限,而django的admin站点中,允许针对某一个用户,可以单独分配权限的。

image-20211025092944971

2. 列表页配置

admin.py里面允许我们编写的代码一共可以分成2部分:列表页配置与详情页配置。

主要用于针对项目中各个子应用里面的models.py里面的模型,根据这些模型自动生成后台运营站点的管理功能。

stuapi/admin.py,代码:

from django.contrib import admin
from .models import Student
# Register your models here.


class StudentModelAdmin(admin.ModelAdmin):
    """学生的模型管理器"""
    pass

# admin.site.register(模型类, 模型管理类)
admin.site.register(Student, StudentModelAdmin)

子应用的英文名改成中文显示,stuapi/apps.py,代码:

from django.apps import AppConfig


class StuapiConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'stuapi'
    verbose_name = "学生管理"
    verbose_name_plural = verbose_name

关于列表页的配置,stuapi/admin.py,代码:

from django.contrib import admin
from . import models


# Register your models here.
class StudentModelAdmin(admin.ModelAdmin):
    """admin站点的模型管理器"""
    """列表页功能配置"""
    # 列表页字段显示[可以使用模型的字段,也可以是模型/管理器的方法]
    list_display = ("id", "name", "age", "sex_display", "sex_display2", "mobile")
    @admin.display(description="性别", ordering="sex")
    def sex_display2(self, obj):
        return "男" if obj.sex else "女"

    # 列表页默认排序字段[写法与ORM的order_by一样]
    ordering = ["-age", "id"]
    # 设置允许点击指定字段可以跳转到详情页[默认是主键]
    list_display_links = ["id", "name"]
    # 设置列表页的过滤字段[只支持模型字段,不支持模型/管理器的方法]
    list_filter = ["sex", "age"]
    # 设置列表页单页数据量
    list_per_page = 10
    # 设置允许在列表页直接编辑的字段[只支持模型字段]
    list_editable = ["age", "mobile"]
    # 设置允许在列表中搜索的字段
    search_fields = ["name", "classmate"]
    # 设置列表页中的日期过滤器
    date_hierarchy = "updated_time"

    # 动作栏是否在上下方显示
    actions_on_top = False     # 上方控制栏是否显示,默认False表示隐藏
    actions_on_bottom = True  # 下方控制栏是否显示,默认False表示隐藏

    # 详情页功能配置


admin.site.register(models.Student, StudentModelAdmin)

stuapi/models.py,代码:

from django.db import models


class Student(models.Model):
    STATUS = (
        (0, "正常入学"),
        (1, "正常毕业"),
        (2, "已经辍学"),
    )
    name = models.CharField(max_length=15, verbose_name="学生名字")
    age = models.SmallIntegerField(verbose_name="年龄")
    sex = models.BooleanField(default=True, verbose_name="性别")
    classmate = models.CharField(db_column="class", max_length=50, verbose_name="班级")
    mobile = models.CharField(max_length=20, unique=True, verbose_name="手机号码", help_text="请输入一个唯一的手机号码")
    description = models.TextField(null=True, verbose_name="个性签名")
    status = models.SmallIntegerField(null=True, verbose_name="状态码")
    created_time = models.DateTimeField(auto_now_add=True)
    updated_time = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "ww_student"
        verbose_name = "学生信息"
        verbose_name_plural = verbose_name

    def __str__(self):
        return f"{self.name}"

    def sex_display(self):
        return "男" if self.sex else "女"

    # 设置方法名在列表页中的文字提示
    sex_display.short_description = "性别"
    # 设置方法名在列表页中的排序字段
    sex_display.admin_order_field = "sex"

数据库和本地Django的时区有冲突,所以需要根据实际开发过程中的业务来考虑是否要修改时区关闭USE_TZ,settings.py,代码:

USE_TZ = False

3. 详情页配置

from django.contrib import admin
from . import models


# Register your models here.
class StudentModelAdmin(admin.ModelAdmin):
    """admin站点的模型管理器"""
    """列表页功能配置"""
    # 列表页字段显示[可以使用模型的字段,也可以是模型/管理器的方法]
    list_display = ("id", "name", "age", "sex_display", "sex_display2", "mobile")
    @admin.display(description="性别", ordering="sex")
    def sex_display2(self, obj):
        return "男" if obj.sex else "女"

    # 列表页默认排序字段[写法与ORM的order_by一样]
    ordering = ["-age", "id"]
    # 设置允许点击指定字段可以跳转到详情页[默认是主键]
    list_display_links = ["id", "name"]
    # 设置列表页的过滤字段[只支持模型字段,不支持模型/管理器的方法]
    list_filter = ["sex", "age"]
    # 设置列表页单页数据量
    list_per_page = 10
    # 设置允许在列表页直接编辑的字段[只支持模型字段]
    list_editable = ["age", "mobile"]
    # 设置允许在列表中搜索的字段
    search_fields = ["name", "classmate"]
    # 设置列表页中的日期过滤器
    date_hierarchy = "updated_time"

    # 动作栏是否在上下方显示
    actions_on_top = False     # 上方控制栏是否显示,默认False表示隐藏
    actions_on_bottom = True  # 下方控制栏是否显示,默认False表示隐藏

    """详情页功能配置"""
    # 设置添加页面和更新页面,显示表单中的字段列表
    # fields = ["name", "sex", "age", "classmate", "description", "mobile", "status"]

    # 设置添加页面和更新页面,显示表单中所报的字段分组列表
    fieldsets = (
        # ["组名", {
        #   "fields": ["字段1","字段2",...] # 当前组出现的字段
        #   "classes": ["collapse", "wide", "extrapretty"] # 折叠样式
        # }],
        ["必填项", {
            "fields": ["name", "age", "mobile", "classmate", "status"],
            "classes": ["wide"],
        }],
        ["可选项", {
            "fields": ["sex", "description"],
            "classes": ["collapse", "wide"],
        }],
    )

    # 添加数据的字段列配置
    add_fieldsets = (
        ("必填项", {
            'fields': ('name', 'age', 'classmate', "mobile"),
            'classes': ('wide',),
        }),
        ('可选项', {
            'fields': ('sex', 'description'),
            'classes': ('collapse',),  # 折叠样式
        }),
    )

    def get_fieldsets(self, request, obj=None):
        """
        :request 本次客户端的请求处理对象
        :obj     本次客户端更新的模型对象[如果本次操作属于添加数据操作,则obj的值为None]
        """
        if obj is None:
            """添加页面的字段列表"""
            return self.add_fieldsets
        else:
            """更新页面的字段列表"""
            return self.fieldsets

    # 钩子方法
    def save_model(self, request, obj, form, change):
        """
        钩子方法:客户端提交保存数据[添加/更新]的表单后自动执行这个方法,
        :request 客户端本次提交的请求对象
        :obj     客户端本次操作的模型对象[如果是添加对象,则当前obj属于刚创建的没有ID的对象]
        :form    客户端本次显示的表单对象[开始]
        :change  客户端本次提交的表单对象[提交]
        """
        # print("客户端提交的数据", request.POST)
        # 更新页面提交的obj.id是数字,添加页面提交obj.id是None
        # print("客户端提交的数据", obj.id, type(obj))
        if obj.id:
            print("更新模型操作")
        else:
            print("添加模型操作")

        # 这里一般用于写验证
        if obj.age > 100:
            raise TypeError("年龄太大了!")

        obj.save()  # 这里模型添加

        # 这里一般写关联模型[关联业务的代码,注册用户发送通知邮件,赠送数据]

    def delete_model(self, request, obj):
        """
        钩子方法:客户端在详情页删除数据时自动执行这个方法,
        :request 客户端本次提交的请求对象
        :obj     客户端本次操作的模型对象
        """
        # 删除前的代码[例如,判断权限,]
        print("详情页删除模型前的代码操作")
        obj.delete()  # 删除代码
        # 删除后的代码[例如,记录操作日志,删除相关其他的数据]
        print("详情页删除模型后的代码操作")

    def delete_queryset(self, request, queryset):
        """
        钩子方法:  客户端在列表页删除数据时自动执行这个方法,
        :request  客户端本次提交的请求对象
        :queryset 客户端本次操作的QuerySet对象[这个QuerySet表示可以1个对象,也可以多个对象]
        """
        # 删除前的代码[例如,判断权限,]
        print("列表页删除模型前的代码操作")
        queryset.delete()  # 删除代码
        # 删除后的代码[例如,记录操作日志,删除相关其他的数据]
        print("列表页删除模型后的代码操作")


admin.site.register(models.Student, StudentModelAdmin)

4. SimpleUI

SimpleUI是最流行的DjangoAdmin站点的第三方模块,是一个比DjangoAdmin默认界面更漂亮更好看,使用更方便的后台站点。

构建于admin站点之上。除了simpleui以外,django2.0时代还有一个XAdmin模块,但是现在已经不再更新维护了。

文档:https://newpanjing.github.io/simpleui_docs

github:https://github.com/newpanjing/simpleui

通过如下命令安装

pip install django-simpleui -i https://pypi.tuna.tsinghua.edu.cn/simple

在配置文件settings.py中注册如下应用

INSTALLED_APPS = [
    'simpleui', # admin界面美化,必须写在admin上面
    'django.contrib.admin', # 内置的admin运营站点
    # ...
]

# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'

# 修改时区
TIME_ZONE = 'Asia/Shanghai'

注册完成以后,直接访问原来的站点即可查看效果:

image-20210622120344117

simpleUI的基本配置,settings.py,代码:

"""simpleUI配置"""
# 是否启动登录页的粒子动画
SIMPLEUI_LOGIN_PARTICLES = False

from django.contrib.admin import AdminSite
# 设置页面中的站点标题
AdminSite.site_header = "北京xxx科技有限公司"
# 设置HTML网页中title标签标签
AdminSite.site_title = "北京xxx科技有限公司"
# 网站的LOGO
SIMPLEUI_LOGO = 'https://avatars2.githubusercontent.com/u/13655483?s=60&v=4'
# 后台左上角房子图标点击跳转的站点首页
# SIMPLEUI_HOME_PAGE = 'http://127.0.0.1:8000/admin/'
# 首页标题
SIMPLEUI_HOME_TITLE = "北京xxxx股份有限公司"
# 首页图标
SIMPLEUI_HOME_ICON = 'fa fa-house'
# 关闭simple的面板信息
SIMPLEUI_HOME_INFO = False
# 关闭simpleui使用分析
SIMPLEUI_ANALYSIS = False
# 使用离线模式
SIMPLEUI_STATIC_OFFLINE = True