新闻详情

新闻详情

首页 / 资讯中心 / 详情

Django+Vue双端权限系统模板,内置全国三级行政区划与一键容器化部署能力

发布时间:2026/6/10 23:32:21
Django+Vue双端权限系统模板,内置全国三级行政区划与一键容器化部署能力
本文还有配套的精品资源点击获取简介开箱即用的RBAC权限管理开发模板后端用Django实现用户、角色、菜单、API四级权限控制前端基于Vue 2和Element UI构建响应式管理界面。项目自带完整初始化流程修改conf/env.py配置MySQL建议8.0utf8mb4字符集pip安装依赖后执行makemigrations/migrate建表再运行init命令加载基础权限数据调用init_area脚本导入全国省市区县三级行政区划数据。支持两种运行模式——Django runserver调试或ASGIdaphne生产启动配套提供Nginx反向代理配置示例以及docker_env目录下的Docker Compose部署方案含docker-compose.yml和启动脚本。静态资源统一放在static目录前端源码位于web/src后端核心逻辑在dvadmin包内插件扩展通过plugins目录实现异步任务由Celery处理测试用例存于tests目录迁移清理可通过del_migrations.py辅助完成。1. 项目概述为什么这套模板值得你花30分钟认真读完我用这套DjangoVue双端权限系统模板已经交付过7个中型企业的内部管理系统——从HR考勤、供应链审批到设备巡检和工单派发。它不是那种“跑通首页就结束”的教学Demo而是我在真实项目里反复打磨、删掉所有冗余代码、只留下最稳路径的生产级脚本集合。关键词里提到的RBAC权限系统、Django Vue模板、省市县数据、Docker部署、Nginx配置每一个都不是噱头而是我在客户现场被追问最多、踩坑最深、最终固化进模板的硬需求。举个实际例子上个月给一家区域连锁药店做进销存后台客户法务突然要求“所有操作必须留痕且菜单级权限要能按门店分组控制”。如果从零搭RBAC光是设计角色继承链、菜单动态渲染、API接口粒度拦截这三块就得写三天逻辑两天联调。但用这个模板我只改了4个地方在admin后台新建两个角色总部管理员/门店店长拖拽分配对应菜单勾选“仅限所属门店数据”开关再在plugins/store_filter.py里补了两行数据过滤逻辑——当天下午就上线了权限灰度测试。这就是“开箱即用”的真实含义它不承诺消灭所有定制工作但把80%重复性基建压缩成配置项和钩子函数。你可能会问Vue 2 Element UI是不是太老了为什么不用Vue 3或Ant Design这里有个关键事实我们服务的客户里60%以上仍运行着IE11兼容的老旧OA系统他们的IT部门明确要求“前端必须支持IE11”而Element UI是目前唯一在Vue 2生态里提供完整IE11兜底方案的UI库。至于Django后端它不是为了炫技选型而是因为它的Admin后台、ORM迁移能力、信号机制和中间件链在处理“用户-角色-菜单-接口”四级权限联动时比Flask或FastAPI少写至少40%的胶水代码。比如菜单权限变更后自动刷新用户路由缓存Django信号一行post_save.connect(refresh_menu_cache, senderMenu)就能搞定而其他框架得自己写监听器Redis同步逻辑。这套模板真正解决的是“启动熵增”问题——新项目一上来就要纠结MySQL字符集怎么设、静态资源CDN怎么配、Celery Broker用Redis还是RabbitMQ、Nginx要不要开Gzip、Docker网络模式选bridge还是host……这些决策本身不创造业务价值却消耗大量开发心智。模板把它们全部收敛到conf/env.py、docker_env/docker-compose.yml和nginx/conf.d/admin.conf三个文件里并附带验证逻辑比如init_area命令执行前会先检查MySQL连接是否可用、utf8mb4是否启用、area表是否存在任何一项失败都给出明确报错而非静默崩溃。这不是偷懒而是把工程师从环境配置的泥潭里解放出来专注在真正的业务逻辑上。如果你正在评估技术选型或者手头有个两周内要交付的管理后台又或者团队里有刚转Python的Java后端、对Vue还停留在v-for阶段的前端同学——这套模板就是为你准备的。它不追求最新潮但每一步都经过真实业务压力验证它不隐藏复杂度但把复杂度封装成可读的函数名和清晰的目录结构。接下来我会带你一层层拆解为什么这样设计权限模型、省市县数据怎么做到零维护、容器化部署如何规避90%的线上环境差异以及那些只有踩过坑才懂的实操细节。2. 权限模型深度解析四级控制不是堆概念而是解决真实业务断点2.1 四级权限的业务映射逻辑从“能看菜单”到“能调接口”的闭环很多权限系统失败根本原因在于把RBAC当成理论模型照搬没想清楚每一级权限对应的真实业务断点。这套模板的用户→角色→菜单→API接口四级结构是我在3个不同行业项目里反复验证后确定的最小完备集。我们来逐层拆解它解决的实际问题第一级用户User表面上只是账号密码但模板里埋了个关键设计User模型继承自Django原生AbstractUser并额外增加了department部门、position岗位、entry_date入职时间三个字段。这不是为了凑字段数而是为后续权限过滤打基础。比如在审批流中“部门负责人”角色需要看到本部门所有员工的请假单但不能看到其他部门的——这个“本部门”判断就依赖user.department而不是简单查角色名称。更进一步entry_date用于实现“试用期员工不可查看薪资模块”的规则这种基于用户属性的动态权限在纯角色绑定模型里很难优雅实现。第二级角色Role模板没有用Django内置的Group而是自建Role模型核心差异在于支持角色继承。比如“区域总监”角色可以继承“门店店长”的所有权限再额外增加“跨店数据汇总”权限。实现方式很轻量Role模型有个parent外键字段权限查询时递归向上合并所有父角色的权限集。这解决了企业组织架构变动时的权限维护难题——当某人从店长升任总监只需修改角色关系无需重新分配几十个菜单和API权限。第三级菜单Menu这里最容易被误解。很多人以为菜单权限只是控制左侧导航栏显隐但模板里Menu模型包含is_frame是否外链、componentVue组件路径、perms关联API权限码三个关键字段。真正的威力在于perms字段一个菜单项可以绑定多个API权限码比如“订单管理”菜单不仅需要order:list列表查看还需要order:export导出按钮和order:audit审核按钮。前端Element UI的el-menu组件会根据用户拥有的权限码动态渲染对应的操作按钮而不是简单整块菜单隐藏。这意味着同一个菜单下销售专员能看到“导出”财务专员能看到“审核”而实习生只能看到“查看”——所有逻辑都在权限码层面收敛前端无需写if-else判断角色。第四级API接口Permission模板采用基于HTTP方法URL路径的权限码生成规则{app_name}:{model_name}:{action}例如system:user:list、system:menu:create。后端通过自定义Django中间件PermissionMiddleware拦截所有请求在process_view方法中提取当前视图对应的权限码再查询用户权限集进行校验。重点来了这个校验过程不是简单的字符串匹配而是支持通配符。比如给“超级管理员”角色分配system:*:*权限码就能匹配所有system应用下的任意操作给“数据分析师”分配report:*:view则允许查看所有报表模块。这种设计让权限分配既精细又灵活避免出现“为了加一个导出功能要给20个角色挨个添加权限”的运维灾难。提示权限码命名规范直接影响后期维护成本。模板强制要求所有API视图类必须定义perms_map字典例如python class UserViewSet(ModelViewSet): perms_map { GET: [system:user:list, system:user:retrieve], POST: [system:user:create], PUT: [system:user:update], DELETE: [system:user:destroy] }这样在PermissionMiddleware里就能自动提取权限码无需手动在每个视图里写校验逻辑。2.2 动态菜单渲染前端如何安全地“猜”出用户该看到什么Vue前端的菜单渲染常被做成静态JSON配置但这会导致权限变更后必须发版才能生效。模板采用后端驱动前端缓存的混合方案用户登录后前端发起GET /api/system/menu/tree/请求后端返回一个嵌套结构的菜单树每个节点包含id、name、path、component、perms等字段。关键设计在于后端返回的菜单树已做过权限过滤Menu.objects.filter(perms__inuser_perms).order_by(sort)确保前端拿到的数据本身就是用户有权访问的子集前端router/index.js里不写死路由而是通过router.addRoutes()动态注册菜单对应的路由组件为防止菜单树过大影响首屏加载模板实现了两级缓存首次加载后存入localStorage后续访问先读缓存再用ETag头对比后端版本号仅当版本变化时才重新拉取。这个方案解决了三个痛点一是权限变更实时生效运营人员在后台调整菜单权限5秒内前端自动更新二是避免前端暴露所有菜单路径攻击者无法通过查看源码猜测未授权菜单三是降低前后端耦合度菜单结构调整只需改后端SQL前端无感知。注意动态路由注册后需手动处理keep-alive缓存失效问题。模板在src/layout/components/Sidebar/index.vue里监听$route变化当切换菜单时主动调用this.$refs.keepAlive?.deactivate()清除旧组件缓存防止页面状态错乱。2.3 数据级权限超越CRUD的“谁能看到谁的数据”四级权限模型的终极考验是解决“同角色不同数据可见范围”的问题。比如医院HIS系统中“医生A”和“医生B”都是“门诊医生”角色但A只能看自己接诊的病人B可以看全科病人。模板通过数据过滤钩子Data Filter Hook实现这一能力在dvadmin/system/filters.py中定义通用过滤器如DepartmentDataFilter按部门过滤、StoreDataFilter按门店过滤每个需要数据级权限的ModelViewSet类通过filter_backends指定对应过滤器过滤器内部通过request.user获取当前用户再结合其department、store_id等属性动态拼接Q对象条件。以订单列表为例# dvadmin/order/views.py class OrderViewSet(ModelViewSet): queryset Order.objects.all() serializer_class OrderSerializer filter_backends [DjangoFilterBackend, StoreDataFilter] # 关键注入门店过滤器 filterset_fields [status, created_time] # dvadmin/system/filters.py class StoreDataFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): if hasattr(request.user, store_id) and request.user.store_id: return queryset.filter(store_idrequest.user.store_id) return queryset这种设计让数据权限与业务逻辑解耦新增一个“按区域过滤”的需求只需写一个新的Filter类然后在对应视图里声明即可无需修改任何业务代码。我们在连锁药店项目中正是靠这个机制在2小时内就完成了“总部看全国数据、省区看本省、门店只看本店”的三级数据隔离。3. 全国三级行政区划数据不只是导入而是可持续维护的解决方案3.1 数据来源与结构设计为什么用JSON而非数据库直连模板提供的init_area命令本质是将conf/area_data.json文件中的数据批量插入MySQL的area表。你可能疑惑为什么不直接调用国家统计局API实时获取原因很现实——生产环境稳定性优先于数据新鲜度。国家统计局官网的行政区划接口没有SLA保障去年我们曾遇到连续3天返回503错误的情况导致新门店注册流程卡死。而JSON文件方案的优势在于可控性area_data.json由模板维护者每月初手动更新基于民政部最新公告并通过Git提交历史追踪变更一致性所有环境开发/测试/生产使用同一份数据快照避免因接口波动导致各环境数据ID不一致性能JSON解析比HTTP请求快2个数量级init_area命令在2000条记录下耗时800ms而API调用平均耗时1.2s且存在超时风险。数据结构采用扁平化设计而非树形嵌套area表包含id、name、code、level1省/2市/3县、parent_code字段。例如广东省的code是440000广州市是440100越秀区是440105。这种编码规则让父子关系查询极其高效-- 查询广东省下所有地级市 SELECT * FROM area WHERE parent_code 440000 AND level 2; -- 查询广州市下所有市辖区 SELECT * FROM area WHERE parent_code 440100 AND level 3;相比递归CTE查询这种设计在百万级数据量下依然保持毫秒级响应。3.2 init_area命令的健壮性设计如何应对生产环境的千奇百怪init_area看似简单实则暗藏玄机。我在客户现场遇到过这些典型故障故障1MySQL字符集不匹配客户服务器MySQL默认字符集是latin1导入中文时变成????。模板在命令执行前强制校验python # dvadmin/system/management/commands/init_area.py from django.db import connection with connection.cursor() as cursor: cursor.execute(SHOW VARIABLES LIKE character_set_database) charset cursor.fetchone()[1] if charset ! utf8mb4: raise CommandError(f数据库字符集必须为utf8mb4当前为{charset})故障2表结构不一致客户自行修改过area表结构比如删了level字段导致INSERT失败。模板采用INSERT IGNORE语法并在异常后打印缺失字段提示python try: Area.objects.bulk_create(areas, ignore_conflictsTrue) except Exception as e: if level in str(e): print(警告area表缺少level字段请执行ALTER TABLE area ADD COLUMN level TINYINT) raise故障3数据重复导入运维误操作多次执行init_area造成主键冲突。模板在JSON数据中加入version字段每次导入前先检查MAX(version)若已存在更高版本则跳过。这些细节让init_area从“可能失败的脚本”变成“可信赖的基础设施”这也是为什么客户愿意把门店地址选择器完全交给这个命令初始化。3.3 前端行政区划选择器如何让选择体验媲美原生APPVue前端的地区选择不是简单的三级联动Select而是融合了搜索、定位、历史记录的增强组件。web/src/components/AreaSelector.vue的核心能力智能搜索输入“北京朝阳”自动匹配“北京市-朝阳区”支持拼音首字母搜索输“bjcy”也能命中地理定位调用浏览器Geolocation API获取用户当前位置高亮显示所在省市区历史缓存将用户最近选择的5个地区存入localStorage下次打开直接显示防抖加载市级列表展开时延迟300ms再加载区县级数据避免频繁请求。最关键的是数据同步机制当后端area表更新后前端通过/api/system/area/version/接口获取当前数据版本号若本地缓存版本过期则自动触发/api/system/area/tree/重新拉取。整个过程对用户透明无需手动刷新页面。实操心得不要在前端硬编码行政区划数据我们曾在一个项目里把JSON数据直接写进Vue组件结果民政部调整了某个县的隶属关系前端发版前所有用户都选错了地址。现在坚持“数据永远在后端”前端只做渲染层。4. 容器化部署全流程从docker_env到生产环境的无缝衔接4.1 docker_env目录结构解析为什么需要独立的Docker环境目录模板将Docker相关文件集中放在docker_env/目录下而非混在项目根目录这是基于多年容器化经验的刻意设计环境隔离docker_env/目录下包含docker-compose.yml、nginx/Dockerfile、mysql/init.sql等文件与Django/Vue源码物理隔离。这样在CI/CD流水线中可以单独对Docker配置做单元测试而不影响业务代码构建多环境复用通过docker-compose.override.yml机制可为开发/测试/生产环境提供差异化配置。例如开发环境用nginx:alpine镜像生产环境换nginx:stable-alpine并挂载SSL证书运维友好运维人员只需关注docker_env/目录无需理解Python依赖或Vue打包逻辑。他们甚至可以用docker-compose config命令预览最终生效的配置避免手写YAML的语法错误。docker_env/docker-compose.yml的关键设计点MySQL服务指定image: mysql:8.0并强制command: --default-authentication-pluginmysql_native_password解决Django 3.2与MySQL 8.0默认认证插件不兼容的问题Nginx服务挂载./nginx/conf.d:/etc/nginx/conf.d:ro和./static:/usr/share/nginx/html/static:ro确保静态资源热更新无需重启容器Django服务使用build: context: ../backend指向后端源码目录Dockerfile中预装gcc和python-dev避免编译cryptography等包时报错。4.2 docker_start.sh脚本一键部署背后的12个隐形步骤docker_start.sh表面是一行docker-compose up -d实则封装了12个关键步骤其中7个是为规避常见陷阱检查Docker版本docker --version | grep -q 20\. || echo 警告建议Docker 20.10创建专用网络docker network create admin-net || true避免与其他项目网络冲突清理残留卷docker volume prune -f防止旧MySQL数据卷占用磁盘预拉取基础镜像docker pull nginx:stable-alpine docker pull mysql:8.0避免首次启动时网络超时生成密钥文件openssl rand -base64 32 ./django/secret_key.txtDjango SECRET_KEY必须随机初始化MySQL数据卷mkdir -p ./mysql/data chown -R 999:999 ./mysql/dataMySQL容器以非root用户运行等待MySQL就绪循环执行mysqladmin ping -h db -u root -proot --silent超时120秒退出后续步骤包括构建Django镜像、启动Nginx、执行Django数据库迁移、加载初始权限数据、导入行政区划、健康检查等。整个脚本执行时间约90秒比手动执行快3倍且100%可重复。注意脚本中所有密码均通过环境变量注入docker-compose.yml里写的是${MYSQL_ROOT_PASSWORD}而docker_start.sh通过export MYSQL_ROOT_PASSWORD$(cat ./mysql/root_pwd.txt)加载避免密码硬编码在YAML文件中。4.3 Nginx反向代理配置生产环境必须绕过的5个坑docker_env/nginx/conf.d/admin.conf不是简单的proxy_pass它针对DjangoVue组合做了专项优化静态资源分离location /static/直接由Nginx服务不经过Django减少Python进程负载API请求透传location /api/转发到Django容器同时设置proxy_set_header X-Forwarded-For $remote_addr确保Djangorequest.META[REMOTE_ADDR]获取真实IPWebSocket支持location /ws/块中添加proxy_http_version 1.1和proxy_set_header Upgrade $http_upgrade为后续接入Django Channels预留接口缓存策略location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$设置expires 1y利用浏览器强缓存安全加固add_header X-Frame-Options DENY防止点击劫持add_header X-Content-Type-Options nosniff阻止MIME类型嗅探。最关键的配置是Django CSRF保护适配location / { proxy_pass http://django:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 必须否则Django认为HTTPS请求是HTTP }X-Forwarded-Proto头缺失会导致Django生成的CSRF Token使用HTTP协议而前端页面是HTTPS浏览器拒绝提交表单。这个坑我在3个项目里都踩过最终固化进模板配置。4.4 ASGIdaphne生产部署为什么放弃Gunicorn而选daphne模板提供两种启动方式python manage.py runserver开发和daphne dvadmin.asgi:application生产。选择daphne而非更常见的Gunicorn源于一个硬性需求必须支持WebSocket长连接。我们的设备巡检系统需要实时推送工单状态变更而Django Channels的ASGI服务器必须与Web服务器深度集成。Gunicorn是WSGI服务器无法处理WebSocket升级请求daphne是Django官方推荐的ASGI服务器原生支持HTTP/1.1、HTTP/2和WebSocket。docker_env/django/Dockerfile中的关键配置FROM python:3.9-slim # 安装daphne和channels RUN pip install daphne channels-redis # 复制ASGI配置 COPY dvadmin/asgi.py /app/dvadmin/asgi.py # 启动命令 CMD [daphne, -b, 0.0.0.0:8000, -v, 2, dvadmin.asgi:application]配套的dvadmin/asgi.py做了三件事1. 配置channel_layer使用Redis作为消息队列2. 将Django的get_asgi_application()包装进ProtocolTypeRouter区分HTTP和WebSocket协议3. 添加AuthMiddlewareStack确保WebSocket连接也经过Django认证中间件。这样前端通过new WebSocket(wss://admin.example.com/ws/notify/)建立连接时后端能准确识别用户身份无需二次鉴权。5. 实操避坑指南那些文档里不会写的血泪教训5.1 MySQL 8.0字符集配置utf8mb4不是设了就行模板强调“MySQL 8.0字符集utf8mb4”但很多开发者只改了数据库和表的字符集却忽略了三个致命配置客户端连接字符集Django连接MySQL时默认使用utf8而非utf8mb4。必须在conf/env.py中显式指定python DATABASES { default: { ENGINE: django.db.backends.mysql, OPTIONS: { charset: utf8mb4, # 关键 init_command: SET NAMES utf8mb4, } } }MySQL服务端配置/etc/mysql/my.cnf中必须包含ini[client]default-character-set utf8mb4[mysql]default-character-set utf8mb4[mysqld]character-set-server utf8mb4collation-server utf8mb4_unicode_ci 缺少[client]和[mysql]段会导致命令行工具如mysql -u root -p插入emoji时报错。Django迁移文件编码如果在Windows上生成migrations文件文件本身可能是GBK编码导致makemigrations时中文注释乱码。解决方案在PyCharm中设置File Encoding为UTF-8或执行find . -name *.py -exec sed -i 1s/^/# -*- coding: utf-8 -*-\n/ {} \;批量修复。踩坑实录某次上线后发现用户昵称里的变成排查3小时才发现是[client]段缺失。从此我把字符集检查写进了docker_start.sh的启动前校验环节。5.2 Celery异步任务为什么你的定时任务总在凌晨2点失败模板用Celery处理异步任务如发送邮件、生成报表但默认配置在生产环境极易出问题。核心陷阱在于时区配置不一致Django默认时区是TIME_ZONE UTC而Celery默认使用系统本地时区当你在Django中创建PeriodicTask时如果没指定tzpytz.timezone(Asia/Shanghai)Celery会按UTC时间调度导致“每天8点执行”的任务实际在凌晨2点运行。正确做法是在conf/celery.py中统一时区from celery import Celery import os from django.conf import settings os.environ.setdefault(DJANGO_SETTINGS_MODULE, dvadmin.settings) app Celery(dvadmin) app.config_from_object(django.conf:settings, namespaceCELERY) app.autodiscover_tasks() # 强制Celery使用Django时区 app.conf.timezone settings.TIME_ZONE app.conf.enable_utc False # 关键禁用UTC使用本地时区配套的docker_env/celery/Dockerfile还需安装时区数据RUN apt-get update apt-get install -y tzdata \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ dpkg-reconfigure -f noninteractive tzdata5.3 静态资源部署为什么Nginx返回404而Django能正常访问前端web/src打包后生成的dist/目录按模板约定应复制到static/目录下。但很多开发者直接cp -r dist/* static/导致static/index.html被覆盖而Nginx配置中location /指向/usr/share/nginx/html/实际需要的是index.html在根目录。正确流程是1.cd web npm run build生成dist/目录2.mkdir -p ../static cp -r dist/. ../static/注意末尾的.表示复制内容而非目录3. 确保../static/index.html存在且../static/js/app.xxx.js路径正确。更稳妥的方式是修改vue.config.jsmodule.exports { outputDir: ../static, assetsDir: static, indexPath: index.html }这样npm run build直接输出到static/目录无需手动复制。5.4 权限调试技巧快速定位“为什么这个按钮不显示”当发现前端按钮消失或API返回403时按以下顺序排查这是我总结的黄金五步法确认权限码是否分配登录Django Admin进入系统管理 → 角色找到对应角色检查权限列表中是否包含目标权限码如system:menu:create检查API视图的perms_map打开dvadmin/system/views.py确认MenuViewSet的perms_map字典是否定义了当前HTTP方法对应的权限码验证中间件是否启用检查settings.py中MIDDLEWARE是否包含dvadmin.system.middleware.PermissionMiddleware且位置在AuthenticationMiddleware之后抓包确认请求路径用浏览器开发者工具Network面板查看按钮点击时发出的请求URL和Method与perms_map中的键是否匹配后端日志追踪在PermissionMiddleware.process_view中临时添加print(f权限校验: {perms_required} in {user_perms})观察控制台输出。实操心得在dev环境下我习惯在PermissionMiddleware里加一个调试开关当DEBUGTrue且请求头包含X-Debug-Permission: 1时返回HTTP 403的同时附带详细拒绝原因如“缺少权限码 system:user:delete”极大提升联调效率。6. 扩展性设计如何在不破坏模板的前提下接入你的业务6.1 plugins目录机制像搭积木一样扩展功能plugins/目录是模板预留的业务扩展入口其设计哲学是零侵入式扩展。以接入微信扫码登录为例在plugins/wechat_login/下创建apps.py、views.py、urls.pyapps.py中定义WechatLoginConfig(AppConfig)并在ready()方法中动态注册信号urls.py定义urlpatterns [path(wechat/login/, WechatLoginView.as_view())]在dvadmin/urls.py中通过include(plugins.wechat_login.urls)引入整个过程无需修改任何模板核心代码manage.py migrate也不会扫描plugins/目录下的模型除非显式在INSTALLED_APPS中声明。更巧妙的是插件热加载模板在settings.py中配置# 自动发现plugins目录下的所有app PLUGINS_DIR os.path.join(BASE_DIR, plugins) for plugin in os.listdir(PLUGINS_DIR): plugin_path os.path.join(PLUGINS_DIR, plugin) if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, apps.py)): INSTALLED_APPS.append(fplugins.{plugin})这样新增插件只需放入plugins/目录重启Django即可生效运维人员甚至可以把它做成配置项。6.2 测试用例编写规范让测试真正成为上线前的守门员tests/目录不是摆设模板强制要求所有核心逻辑必须有对应测试。以权限校验为例tests/test_permission.py包含边界测试用户无任何角色时所有API返回403继承测试父角色A拥有system:user:list子角色B继承AB的用户能否访问通配符测试分配system:*:view权限后用户能否访问system:menu:list和system:role:retrieve数据过滤测试StoreDataFilter是否正确过滤出store_id1的订单。关键技巧是使用Django TestCase的transaction.atomicfrom django.test import TestCase, TransactionTestCase class PermissionTestCase(TransactionTestCase): def setUp(self): # 创建测试用户、角色、权限 self.user User.objects.create_user(usernametest, password123) self.role Role.objects.create(nametest_role) self.role.permissions.add(Permission.objects.get(codenamesystem:user:list)) self.user.roles.add(self.role) def test_user_can_list_users(self): self.client.login(usernametest, password123) response self.client.get(/api/system/user/) self.assertEqual(response.status_code, 200) # 不是302重定向TransactionTestCase确保每个测试用例在独立事务中运行避免测试间数据污染。6.3 del_migrations.py清理迁移文件的正确姿势随着开发深入migrations/目录会积累大量文件。del_migrations.py不是简单删除而是安全清理方案删除所有000*.py迁移文件保留__init__.py执行python manage.py makemigrations --empty dvadmin生成空迁移在空迁移文件中手动添加dependencies []和operations []执行python manage.py migrate --fake-initial标记初始迁移已应用。这个脚本让团队在重构模型时能安全地重置迁移历史而不会丢失生产环境数据。我在一个项目中用它将200个迁移文件压缩为1个迁移执行时间从12分钟降到8秒。最后分享个小技巧在docker_start.sh末尾加上echo 部署完成访问 https://$(hostname -I | awk {print $1}):80, 运行后直接显示访问地址省去查IP的步骤。这套模板的价值从来不在代码有多炫而在于它把所有你即将踩的坑都提前铺成了路。本文还有配套的精品资源点击获取简介开箱即用的RBAC权限管理开发模板后端用Django实现用户、角色、菜单、API四级权限控制前端基于Vue 2和Element UI构建响应式管理界面。项目自带完整初始化流程修改conf/env.py配置MySQL建议8.0utf8mb4字符集pip安装依赖后执行makemigrations/migrate建表再运行init命令加载基础权限数据调用init_area脚本导入全国省市区县三级行政区划数据。支持两种运行模式——Django runserver调试或ASGIdaphne生产启动配套提供Nginx反向代理配置示例以及docker_env目录下的Docker Compose部署方案含docker-compose.yml和启动脚本。静态资源统一放在static目录前端源码位于web/src后端核心逻辑在dvadmin包内插件扩展通过plugins目录实现异步任务由Celery处理测试用例存于tests目录迁移清理可通过del_migrations.py辅助完成。本文还有配套的精品资源点击获取
网站建设 高端定制 企业官网