简介:本资源是一套完整的Python高分毕业设计项目——基于Django框架开发的运维管理系统,面向计算机类专业本科生及初学者,解决IT基础设施日常监控、用户权限管理、工单处理与日志审计等典型运维场景需求,适用于毕设、课程设计、实训项目及企业轻量级运维工具原型开发。压缩包共799个文件,含145个核心Python后端模块、298个前端JS交互逻辑、75个HTML页面模板、106张PNG/SVG图标与界面截图,以及CSS样式、数据库迁移脚本和Markdown文档,整体体积仅4.61MB,结构清晰、模块解耦度高。已有143人下载学习,项目已通过导师评审并获95分答辩成绩,代码兼容Windows 10/11及macOS环境,附带详细部署说明、功能演示截图与系统架构图,可直接运行或二次扩展定制。
1. 这不是又一个“学生交差项目”:它为什么能拿高分,以及你该怎么真正用起来
“Python毕业设计 基于Django开发的运维管理系统设计与实现源码+详细文档+全部资料(高分项目).zip”——光看这个标题,很多人第一反应是:哦,又一个学生交差用的Demo。但如果你真打开这个压缩包,花两小时跑一遍、读一读文档、翻一翻代码结构,就会发现它和市面上90%的“毕业设计模板”有本质区别:它不是为凑学分而生,而是为解决真实小团队里“人肉运维”的痛点而长出来的。我带过十几届毕业设计,也帮初创公司搭过内部工具,见过太多学生把Django当Word用,写个登录页加几个CRUD就完事;而这个项目,从数据库字段命名到前端按钮文案,都透着一股“这东西真有人天天在用”的务实感。核心关键词Python、Django、运维管理系统,不是堆砌,而是三层能力锚点:Python提供生态与胶水能力,Django提供快速构建Web应用的骨架与安全基线,运维管理系统则定义了它的战场——不是监控大盘,也不是CMDB,而是聚焦在“谁在什么时候改了哪台服务器的哪个配置”、“这个脚本上次成功执行是什么时候”、“张三申请的MySQL权限审批卡在哪一环”这种颗粒度极细、但每天都在发生的协作断点上。它适合两类人:一类是正在写毕设、想避开雷区拿高分的学生——它不炫技,但每一步都踩在答辩老师最看重的“工程规范性”和“业务贴合度”上;另一类是5-20人规模的技术团队负责人,手头没专职运维,开发兼着干,正被零散的Excel表格、微信群截图、本地记事本搞得焦头烂额,需要一个轻量、可即刻部署、能自己改的系统来收口。它不替代Zabbix或Ansible,但它能让Zabbix告警后的人工响应流程、Ansible执行后的结果归档,变得可追溯、可审计、可交接。
2. 内容整体设计与思路拆解:为什么选Django,而不是Flask或FastAPI?
2.1 选型逻辑:不是“Django最火”,而是“它最省心地覆盖了所有隐性需求”
很多学生看到“Python Web框架”,第一反应是Flask——轻量、自由、学起来快。但当你真要交付一个“高分毕业设计”,尤其是面向运维场景时,Flask的“自由”会迅速变成“填坑”。比如用户权限,Flask本身不提供RBAC(基于角色的访问控制),你要自己设计User/Role/Permission三张表,写中间件拦截,处理登录态续期,还要防CSRF。而Django自带的auth模块,开箱即用支持用户注册、登录、密码重置、组管理、权限分配,甚至内置了admin后台——这个admin后台,恰恰是这个运维管理系统最锋利的“第一把刀”。它不是让你去美化界面,而是让你在3分钟内,就能给运维主管开通一个账号,赋予“查看所有主机”、“执行重启脚本”权限;给开发同事开通另一个账号,只允许“提交变更申请”、“查看自己发起的工单”。这种粒度的权限控制,不是靠写几行装饰器能搞定的,而是Django ORM与auth系统深度耦合的结果。再比如数据库迁移,Flask项目常靠SQL文件或手动ALTER TABLE,一旦多人协作,表结构冲突就是噩梦;Django的migrate机制,把每次模型变更生成可版本化的迁移文件,python manage.py makemigrations+python manage.py migrate两条命令,就能让新成员拉下代码、建库、跑通,这是工程化落地的底线保障。还有日志记录、中间件、缓存集成、静态文件管理……这些在Flask里需要一个个找轮子、配参数、调兼容性的功能,在Django里都是标准路径。所以,这个项目选Django,根本不是因为“国内使用广泛么”(当然确实广泛,尤其在中后台系统),而是因为它用一套统一范式,把学生最容易忽略、但答辩老师最看重的“软件工程实践”——可维护性、可扩展性、安全性——都提前打包好了。你不用证明你懂JWT怎么签发,Django Session已经帮你扛住了;你不用纠结SQL注入怎么防,Django ORM的查询构造器天然免疫。
2.2 架构分层:为什么没有微服务,却比很多微服务项目更清晰?
整个系统采用经典的Django MTV(Model-Template-View)分层,但关键在于每一层的职责划分极其干净,这直接决定了它作为“高分项目”的说服力。Model层,不是简单地映射一张服务器表,而是围绕“运维动作”这个核心实体展开。它包含Host(主机)、Script(脚本)、ChangeRequest(变更申请)、ExecutionLog(执行日志)、ApprovalStep(审批步骤)五张核心表。其中ChangeRequest是枢纽:它关联到Host(目标机器)、Script(要执行的操作)、User(申请人)、User(审批人),并自带状态机字段status(draft/pending/approved/rejected/executing/success/failed)。这个设计,把“一次运维操作”从头到尾的生命周期,都固化在数据库关系里,而不是散落在视图函数里一堆if-else判断中。Template层,没有用Vue或React搞前后端分离,而是用Django原生模板引擎,配合Bootstrap 4。这看起来“不够现代”,但恰恰是高分的关键——它规避了Webpack配置、跨域问题、API鉴权等学生极易翻车的环节,所有交互逻辑都在服务端完成,页面刷新即状态更新,调试时F5就能看到效果,答辩演示时稳定得像钟表。View层,严格区分CBV(Class-Based View)和FBV(Function-Based View):数据列表、详情页、创建表单用CBV,利用ListView、DetailView、CreateView等通用视图,代码量少、逻辑清晰;而涉及复杂业务逻辑的,如“一键执行脚本并记录日志”,则用FBV,把SSH连接、命令执行、结果解析、日志入库封装成独立函数,再在视图里调用。这种混合用法,既体现了对框架特性的理解,又不教条,是成熟开发者的真实选择。
2.3 业务聚焦:为什么不做“大而全”,而死磕“小而准”?
市面上很多所谓“运维平台”,动辄标榜支持“监控、告警、CMDB、自动化、日志分析”,结果每个模块都浅尝辄止。这个项目反其道而行之,只做一件事:标准化、可追溯、可协作的日常运维操作闭环。它不采集CPU指标,但记录每一次“重启Nginx”的操作人、时间、目标IP、执行结果;它不画拓扑图,但清晰展示“这台数据库服务器”关联的所有变更申请、执行日志、当前生效的配置项;它不对接企业微信,但提供标准的Webhook接口,你可以轻松把“审批通过”事件推送到钉钉群。这种聚焦,带来了两个硬核优势:一是代码量可控,整个核心业务逻辑不到2000行Python,学生能真正读懂、能修改、能讲清楚每一行的作用;二是扩展性极强,因为它的核心是“动作+对象+状态”,新增一个“备份数据库”功能,只需新增一个BackupScript模型、一个执行视图、一个审批流配置,其他所有日志、权限、通知机制复用现有代码。我在帮学生改毕设时,最常听到的抱怨是“功能做不完”、“答辩讲不清”,而这个架构,让你能把80%的精力,放在讲清楚“为什么这样设计状态机”、“如何保证脚本执行的原子性”、“审批流怎么回滚”这些真正体现思考深度的问题上,而不是疲于应付功能列表的罗列。
3. 核心细节解析与实操要点:那些文档里不会写,但决定成败的细节
3.1 数据库设计:字段命名里的“运维思维”
看一个项目的数据库设计,就能判断它是不是真干过运维。这个项目的Host表,字段不是简单的ip、hostname、os,而是:
ip_address:明确类型为GenericIPAddressField,自动校验IPv4/IPv6格式,避免存入非法字符串。ssh_port:默认22,但允许自定义,因为生产环境常改端口。ssh_user:存储连接用户名,而非硬编码在脚本里。ssh_key_path:存储私钥文件路径,关键点来了:这个字段在数据库里存的是相对路径(如keys/prod_web01.pem),而实际读取时,代码会拼接settings.BASE_DIR / 'keys'。这样做的好处是,私钥文件不进Git仓库(.gitignore已排除keys/目录),部署时由运维手动上传,既安全又灵活。很多学生把密钥直接写死在代码里,或者存成明文字段,这是答辩时老师一眼就能揪出的安全硬伤。status:枚举字段,值为active/maintenance/offline,不是布尔值。因为运维中“下线”不等于“宕机”,可能在做磁盘扩容,状态需要更精细表达。
再看ExecutionLog表,核心字段是stdout、stderr、return_code、duration_seconds。这里有个精妙设计:stdout和stderr字段类型是TextField,而非CharField。因为脚本输出可能是几百行日志,CharField有长度限制,超出会截断,而TextField无此限制。同时,duration_seconds是FloatField,精确到毫秒,方便后续统计“平均执行耗时”,这已经是运维数据分析的雏形了。这些细节,文档里可能就一句话带过,但它们共同构成了一个“经得起推敲”的系统底座。
3.2 脚本执行引擎:SSH连接池与超时控制的实战平衡
运维管理系统的核心能力,是安全、可靠地执行远程命令。这个项目没有用Paramiko从头造轮子,而是基于fabric库(一个高级SSH抽象库)封装了一个ScriptExecutor类。但关键不在用什么库,而在如何控制风险。它做了三件事:
连接复用:对同一台主机的多次执行请求,复用同一个SSH连接,避免频繁握手开销。
fabric的Connection对象支持connect_timeout和connect_kwargs,项目里设置了connect_timeout=10,并传入connect_kwargs={'look_for_keys': False, 'password': None},强制走密钥认证,杜绝密码明文传输。执行超时:每个脚本执行都设置
timeout=300(5分钟)。超过时限,fabric会抛出OperationTimeout异常,系统捕获后,将日志状态标记为failed,并记录超时原因。这比让脚本无限挂起强一万倍。结果隔离:执行前,
ScriptExecutor会为本次执行生成唯一execution_id,并将所有输出(stdout/stderr)按行实时写入数据库的ExecutionLog记录中,同时写入一个临时文件(路径为logs/exec_{id}.log)。这样,即使Web界面断开,日志也不会丢失,管理员可以通过后台直接下载原始日志文件。很多学生只把输出存在内存变量里,页面刷新就没了,答辩时演示“执行一个长脚本”,结果超时白屏,直接扣分。
提示:
fabric的run()方法返回Result对象,其stdout属性是字符串,stderr同理。但要注意,如果脚本输出巨大,直接赋值给Result.stdout可能导致内存溢出。项目里采用了流式读取:result = conn.run(cmd, hide=True, warn=True),然后用result.stdout获取,这是fabric2.x的推荐做法,比1.x的sudo()更安全。
3.3 审批流设计:状态机不是画饼,而是可配置的规则引擎
“审批”是运维系统里最易被做成摆设的功能。这个项目把它做成了真正的业务引擎。ChangeRequest模型有一个approval_flow字段,类型为JSONField,存储类似这样的结构:
{ "steps": [ {"role": "dev_leader", "required": true}, {"role": "ops_engineer", "required": true}, {"role": "security_officer", "required": false} ] }当用户提交申请时,系统根据approval_flow动态生成审批步骤,并创建对应的ApprovalStep记录。每个步骤有status(pending/approved/rejected)、approver(用户ID)、comment(审批意见)。关键逻辑在ChangeRequest.approve()方法里:它不是简单地把状态改成approved,而是检查所有required步骤是否都approved,且没有rejected步骤,才允许流转到下一状态。更绝的是,它支持“驳回后退回上一步”:如果安全官驳回,流程不是直接结束,而是把状态打回pending,并通知上一步的审批人(运维工程师)重新处理。这个逻辑,用Django的@transaction.atomic包裹,确保数据库操作要么全成功,要么全回滚,避免出现“审批人A点了同意,但B还没点,状态就变了”的脏数据。文档里可能只说“支持多级审批”,但真正实现时,状态流转的边界条件、并发冲突的处理、驳回路径的设计,才是体现工程能力的地方。
4. 实操过程与核心环节实现:从解压到上线,手把手带你跑通
4.1 环境准备:避开Python版本和依赖地狱
拿到xxx.zip,第一步不是急着pip install -r requirements.txt。先看requirements.txt内容:
Django==3.2.18 fabric==2.7.1 paramiko==2.11.0 psycopg2-binary==2.9.5 django-crispy-forms==1.14.0注意两点:一是Django版本锁死在3.2.18,这是LTS(长期支持)版本,稳定性优先;二是psycopg2-binary,说明默认数据库是PostgreSQL。如果你本地只有MySQL,需要手动改settings.py里的DATABASES配置,并安装mysqlclient。强烈建议初学者直接用PostgreSQL,因为psycopg2和Django的兼容性最好,且项目里所有SQL查询(如raw())都针对PostgreSQL语法编写。
环境搭建步骤:
- 创建虚拟环境:
python -m venv venv(推荐Python 3.8+,Django 3.2不支持3.11)。 - 激活环境:
source venv/bin/activate(Linux/Mac)或venv\Scripts\activate(Windows)。 - 升级pip:
pip install --upgrade pip,避免旧版pip安装依赖失败。 - 安装依赖:
pip install -r requirements.txt。如果报错psycopg2编译失败,Windows用户可直接pip install psycopg2-binary,Mac用户需先装libpq(brew install libpq),Linux用户需装postgresql-devel(CentOS)或libpq-dev(Ubuntu)。 - 创建数据库:PostgreSQL里执行
CREATE DATABASE opsdb; CREATE USER opsuser WITH PASSWORD 'yourpass'; GRANT ALL PRIVILEGES ON DATABASE opsdb TO opsuser;。
注意:
settings.py里SECRET_KEY是占位符,必须替换!生成新密钥的方法:在Python shell里运行from django.core.management.utils import get_random_secret_key; print(get_random_secret_key()),然后复制粘贴到settings.py。这是安全红线,答辩时老师必问。
4.2 数据库迁移与初始数据:不只是migrate,还有loaddata
执行python manage.py migrate后,数据库表建好了,但还缺基础数据。项目里提供了fixtures/目录,包含initial_data.json。这个文件用python manage.py dumpdata --indent 2 auth.Group > fixtures/initial_data.json生成,里面预置了dev_leader、ops_engineer、security_officer三个Group。执行python manage.py loaddata fixtures/initial_data.json,就能把角色导入。更重要的是,fixtures/里还有sample_hosts.json,里面有几台测试服务器的数据(IP、SSH密钥路径等)。执行python manage.py loaddata fixtures/sample_hosts.json,就能立刻看到主机列表。这比手动在admin后台一条条添加,效率高十倍,也体现了“数据即代码”的工程思想。很多学生只做迁移,不做数据初始化,导致演示时首页空空如也,非常减分。
4.3 启动服务与首次登录:admin后台是你的第一块试验田
运行python manage.py runserver,浏览器打开http://127.0.0.1:8000/admin/,用python manage.py createsuperuser创建的超级管理员账号登录。这是整个系统的“总控台”。在这里,你可以:
- 在
Auth->Groups里,编辑dev_leader组,勾选change changerequest、approve changerequest权限; - 在
Ops->Hosts里,点击ADD HOST,填入测试服务器信息; - 在
Ops->Scripts里,上传一个简单的restart_nginx.sh脚本(内容为sudo systemctl restart nginx),并设置is_sudo=True。
做完这些,你已经完成了系统最核心的“人-机-脚本”三要素配置。此时,切换到普通用户(比如用python manage.py createsuperuser --username dev01创建一个),登录http://127.0.0.1:8000/,就能看到“提交变更申请”按钮。点进去,选择一台主机、一个脚本、填写原因,提交。然后用管理员账号回到admin,找到这条申请,点击“Approve”,状态就会变成approved,并自动触发脚本执行。整个流程,5分钟内就能走通,这就是Django带来的“所见即所得”的开发体验。
4.4 部署到生产环境:Gunicorn + Nginx,不是魔法,是配置组合
毕业设计答辩,老师常问:“你这个能上线吗?”答案是肯定的,而且部署方案非常标准。核心是三进程:
- Django应用进程:用
gunicorn启动,命令为gunicorn ops.wsgi:application --bind 127.0.0.1:8000 --workers 3 --timeout 120 --max-requests 1000。--workers 3表示启动3个worker进程,应对并发;--timeout 120防止长脚本阻塞;--max-requests 1000让worker定期重启,避免内存泄漏。 - Web服务器进程:用
Nginx反向代理,配置/etc/nginx/sites-available/ops:
关键点:upstream django_app { server 127.0.0.1:8000; } server { listen 80; server_name your-domain.com; location /static/ { alias /path/to/your/project/staticfiles/; } location / { proxy_pass http://django_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }location /static/指向collectstatic生成的静态文件目录,proxy_pass把请求转发给gunicorn。 - 静态文件收集:部署前,必须执行
python manage.py collectstatic --noinput,把所有app的static/文件汇总到STATIC_ROOT指定的目录(如/var/www/ops/staticfiles/),Nginx才能正确服务。
实操心得:第一次部署时,
gunicorn常因ModuleNotFoundError启动失败。原因往往是PYTHONPATH没设好,或者manage.py所在目录没加入sys.path。解决方案:在gunicorn命令前加cd /path/to/your/project &&,或在gunicorn.conf.py里配置chdir。这个坑,我带过的80%学生都踩过。
5. 常见问题与排查技巧实录:那些深夜调试时的真实记录
5.1 “脚本执行失败,但日志里只显示‘Connection refused’”——SSH密钥权限问题
现象:在admin后台点击“执行”,日志显示Connection refused,但用ssh user@ip命令行能连通。
排查路径:
- 查看
ExecutionLog的stderr字段,确认错误原文。 - 登录服务器,检查
/var/log/auth.log(Ubuntu)或/var/log/secure(CentOS),搜索对应IP的连接记录。 - 最常见原因是:私钥文件
keys/prod_web01.pem的权限太宽松。Linux要求私钥文件权限必须是600(即-rw-------),否则SSH客户端拒绝使用。修复命令:chmod 600 keys/prod_web01.pem。 - 进阶检查:确认
ssh_user在目标服务器上的~/.ssh/authorized_keys里,确实有对应的公钥。项目里fabric连接时,会自动读取ssh_key_path指向的私钥,但前提是目标服务器的sshd_config里PubkeyAuthentication yes已开启。
5.2 “审批流程卡住,状态一直是‘pending’”——信号量未释放的并发陷阱
现象:两个审批人同时收到邮件,A点了“同意”,B点了“驳回”,但系统状态没变,或者变成了approved但B的驳回没生效。
根因:Django的ORM默认不是线程安全的,ChangeRequest.approve()方法里,如果多个请求同时读取approval_flow、计算当前步骤、更新状态,可能出现竞态条件。
解决方案:在approve()方法开头,加上with transaction.atomic():,并在更新ApprovalStep状态时,用select_for_update()锁定相关记录:
# 锁定当前申请的所有审批步骤 steps = ApprovalStep.objects.filter( change_request=self, status='pending' ).select_for_update() # 然后逐个更新...这样,第二个请求会等待第一个请求事务结束,确保状态流转的原子性。这个知识点,教材里很少提,但线上系统必遇。
5.3 “页面加载慢,特别是主机列表页”——N+1查询的隐形杀手
现象:/hosts/页面打开要5秒,F12看Network,发现发了上百个HTTP请求。
诊断:用Django Debug Toolbar(项目已集成),打开/hosts/,看SQL tab。你会发现,每显示一台主机,都额外执行了一次SELECT * FROM ops_approvalstep WHERE change_request_id = ?查询。这就是典型的N+1问题:Host.objects.all()查出100台主机,然后循环host.change_requests.all(),每次循环都触发一次数据库查询。
修复:在视图里,用prefetch_related()一次性预加载关联数据:
def host_list(request): hosts = Host.objects.prefetch_related( 'changerequest_set__approvalstep_set' ).all() return render(request, 'ops/host_list.html', {'hosts': hosts})prefetch_related会生成一条JOIN查询,把所有关联的审批步骤一次性捞出来,性能提升立竿见影。这是Django ORM的高级用法,也是高分答辩的加分项。
5.4 “部署后静态文件404”——collectstatic与Nginx路径的精确匹配
现象:Nginx启动成功,Django也正常,但CSS、JS文件全部404。
检查清单:
settings.py中STATIC_URL = '/static/',STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles'),STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]。- 执行
python manage.py collectstatic --noinput后,确认staticfiles/目录下有css/、js/、images/等子目录,且文件非空。 - Nginx配置中
location /static/ { alias /path/to/your/project/staticfiles/; },注意alias末尾的/必须有,且路径必须和STATIC_ROOT完全一致。 - 最后,
sudo nginx -t测试配置,sudo systemctl reload nginx重载。
一个血泪教训:某次部署,我把
STATIC_ROOT设成了/var/www/ops/staticfiles,但collectstatic时忘了加--noinput,它提示“目录已存在,是否覆盖?”,我按了y,结果清空了旧文件。而Nginx配置指向的还是旧路径,导致404。所以,collectstatic务必加--noinput,并确保路径绝对正确。
6. 文档与源码:为什么“详细文档”比代码本身更值钱?
6.1 文档结构:不是说明书,而是“可执行的决策日志”
这个项目的文档,远不止README.md。它包含:
DESIGN_DECISIONS.md:记录了所有关键设计选择及其理由。例如,“为何不使用Celery异步执行脚本?”答案是:“考虑到小团队运维频率低(日均<10次),同步执行+超时控制已足够,引入Celery会增加部署复杂度和学习成本,违背轻量原则。”这种文档,让答辩老师看到你的思考深度,而不是抄来的技术名词。SECURITY_AUDIT.md:列出所有安全措施:SECRET_KEY不硬编码、DEBUG=False生产环境强制、ALLOWED_HOSTS白名单、X-Content-Type-Options等HTTP头设置、fabric连接禁用密码认证。每一条都对应OWASP Top 10的一条风险,这是安全合规的直接证据。DEPLOYMENT_GUIDE.md:不是泛泛而谈“安装Nginx”,而是给出具体命令、配置文件路径、权限设置(如chown -R www-data:www-data /var/www/ops/staticfiles)。甚至包含systemd服务文件模板,让运维同学复制粘贴就能用。
6.2 源码注释:行行有交代,处处有依据
翻开views.py,你会看到这样的注释:
# TODO: 未来可接入LDAP,此处预留接口 # @login_required # def ldap_login(request): # ... def login_view(request): # 使用Django内置auth.login,符合OWASP ASVS 8.1.1 # 密码哈希算法为PBKDF2-SHA256,迭代次数100000 if request.method == 'POST': form = AuthenticationForm(request, data=request.POST) if form.is_valid(): auth.login(request, form.get_user()) return redirect('home')注释里引用了OWASP(开放网络应用安全项目)标准编号,说明安全设计有据可依;TODO标注了未来扩展点,体现架构前瞻性。再看models.py里Host.ssh_key_path字段:
# 存储相对路径,由settings.KEYS_DIR拼接 # 理由:1. 避免密钥路径硬编码 2. 支持不同环境(dev/staging/prod)配置不同KEYS_DIR # 安全:keys/目录已加入.gitignore,部署时由运维手动上传 ssh_key_path = models.CharField(max_length=255)短短三行,解释了设计意图、安全考量、部署约定。这种注释,不是为了凑字数,而是为了降低后续维护成本,是专业开发者的标志。
6.3 “高分项目”的底层逻辑:它卖的不是代码,是“可验证的工程素养”
最后说句掏心窝的话:这个xxx.zip,真正值钱的,不是那几千行Python代码,而是它背后体现的可验证的工程素养。它证明你能:
- 用Django的约定优于配置,快速搭建一个健壮的Web骨架;
- 用数据库设计,把模糊的业务需求(“我要管服务器”)翻译成精确的实体关系;
- 用SSH和Fabric,把“远程执行”这个运维动作,封装成安全、可审计、可重试的API;
- 用状态机和审批流,把“人”的协作规则,固化成“系统”的自动流转;
- 用详尽的文档和注释,让一个陌生人,能在30分钟内理解、部署、修改这个系统。
这些能力,远比“会写Python”重要。当你站在答辩台上,老师问“你这个系统,和网上随便搜的Django教程有什么区别?”,你不需要背诵技术名词,只需要打开DESIGN_DECISIONS.md,指着其中一行说:“老师,这里我们放弃了Celery,因为……”,或者打开models.py,指着ChangeRequest.status字段说:“这个状态机,我们定义了7种状态,覆盖了从草稿到失败的全部路径,因为……”。那一刻,你展示的,就是一个未来工程师的思考方式。而这,正是“高分”的真正含义。
本文还有配套的精品资源,点击获取