Django入门:从零搭个Python网站,结果被Springboot老哥们笑了?
说实话,作为一个干了快两年DevOps的“脚本仔”,我之前一直对Django这种“大而全”的Web框架敬而远之。我们组后端清一色Springboot,Java系那套微服务、Nacos、Sentinel玩得飞起,连数据库连接池都要压测三轮才敢上线。每次开站会听到后端兄弟们聊什么“Bean生命周期”、“AOP切面日志”,我都默默缩在角落里敲我的Ansible Playbook。
但上周五晚上,产品突然甩过来一个需求:“下周三前搞个内部工具,能录入测试用例、查历史记录就行,别太丑。” 我一看时间——只剩三天!运维同事正在给K8s集群打补丁,前端小哥在改React性能瓶颈,后端?后端正忙着给双11大促压测Springboot服务,谁有空搭新项目?
没办法,只能自己上了。
Python + Django,成了我唯一的选择——快、轻、文档全,还能用我熟悉的VSCode(插件都装好了:Pylance、Django Snippets、Python Docstring Generator……甚至还有个叫“Django Template”的语法高亮,虽然经常抽风)。
于是,就有了这篇血泪踩坑实录。
为什么不用Springboot?因为我不想写100个配置类
先说清楚,我不是黑Java。Springboot确实稳如老狗,但启动速度慢、内存占用高、样板代码多——对于一个临时内部工具来说,简直是杀鸡用牛刀。
我试过用Spring Initializr生成个最简项目,结果光是pom.xml就300多行,还要配JPA、H2、Thymeleaf……而Django呢?
pip install django
django-admin startproject mytool
cd mytool
python manage.py runserver
三行命令,本地8000端口跑起来了。浏览器打开http://127.0.0.1:8000,那个经典的“It worked!”页面让我差点热泪盈眶——这效率,Springboot得配完Maven、IDEA插件、Lombok、启动类、主配置文件才能看到类似效果吧?
第一个坑:你以为的“开箱即用”,其实是“开箱即报错”
刚高兴没两分钟,问题来了。
我想加个用户登录功能,顺手用了Django自带的auth模块。结果部署到测试环境(一台老旧的CentOS 7)时,直接500错误:
django.core.exceptions.ImproperlyConfigured:
The SECRET_KEY setting must not be empty.
我:???本地明明跑得好好的啊!
后来才发现,Django在DEBUG=True时会自动生成一个临时SECRET_KEY,但一旦设为False(生产环境必须关!),就必须手动配置。而我在本地开发时根本没注意这个细节。
解决办法很简单,在settings.py里加一行:
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'your-fallback-key-here')
然后通过环境变量注入——这不就是我们DevOps天天喊的“配置外置”嘛!结果自己写代码时也翻车了,惭愧。
经验教训:永远不要相信“默认能跑”。本地能跑 ≠ 生产能跑。这也是为什么我们组上线前必须过CI/CD流水线,哪怕是个玩具项目。
数据库设计:别以为SQLite能上生产
Django默认用SQLite,开发阶段贼方便,数据直接存成一个.db文件。我一开始偷懒,直接用它跑测试。
结果第二天运维大哥来找我:“你这服务把磁盘IO打满了,数据库文件锁住了,其他服务都卡了!”
我这才意识到:SQLite是单进程、文件级锁,根本不适合并发场景。赶紧换成PostgreSQL。
切换过程其实不难:
- 安装
psycopg2驱动 - 改
settings.py里的DATABASES配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
- 执行迁移:
python manage.py migrate
但这里又踩了个坑:PostgreSQL的字段名大小写敏感!我在Model里定义了一个字段叫testCaseID,结果迁移时Django自动转成小写testcaseid,SQL查询直接报错找不到列。
最后改成全小写+下划线风格:test_case_id,符合PEP8,也避免数据库兼容性问题。
接口设计:RESTful?不,先让前端能调通再说
我们前端用的是Vue3,需要JSON API。我本想直接上Django REST framework(DRF),但时间紧,先用原生View凑合:
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
@csrf_exempt
def create_case(request):
if request.method == 'POST':
data = json.loads(request.body)
# ... 保存逻辑
return JsonResponse({'status': 'ok'})
结果前端调用时报错:403 Forbidden - CSRF token missing。
我:不是加了@csrf_exempt了吗?
查了半天才发现——这个装饰器只对函数视图有效,对类视图无效!而且如果用了中间件全局启用CSRF,还得额外配置。
最后还是乖乖上了DRF,三行代码搞定:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['POST'])
def create_case(request):
# 自动处理JSON解析、CSRF豁免
return Response({'status': 'ok'})
DRF真香!比手搓强一百倍。而且它自动生成API文档(配合drf-yasg),前端小哥再也不用追着我问接口字段了。
部署上线:从runserver到Gunicorn + Nginx
本地runserver只是开发服务器,绝对不能用于生产!我第一次直接用它部署,结果并发一高就502,日志里全是BlockingIOError。
正确的姿势是:Gunicorn(WSGI服务器) + Nginx(反向代理)。
先装Gunicorn:
pip install gunicorn
然后启动:
gunicorn --bind 0.0.0.0:8000 mytool.wsgi:application
再配Nginx:
server {
listen 80;
server_name mytool.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static/ {
alias /path/to/mytool/staticfiles/;
}
}
注意:静态文件要单独收集!执行:
python manage.py collectstatic
否则CSS/JS全404,页面丑得像90年代网页。
性能监控:别等线上崩了才看日志
上线后,我习惯性地加了Prometheus监控。Django有个插件叫django-prometheus,几行配置就能暴露指标:
# settings.py
INSTALLED_APPS = [
...
'django_prometheus',
]
MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
# ... 其他中间件
'django_prometheus.middleware.PrometheusAfterMiddleware',
]
然后在urls.py加个endpoint:
from django.urls import path, include
urlpatterns = [
path('', include('myapp.urls')),
path('metrics/', include('django_prometheus.urls')),
]
这样,Prometheus就能抓取请求延迟、DB查询次数、缓存命中率等指标。配合Grafana,一眼看出性能瓶颈。
比如我发现某个接口平均响应时间2秒,查指标发现数据库查询高达50次——典型的N+1问题!用select_related()优化后,降到300ms。
和Springboot对比:没有银弹,只有合适
有人问我:Django和Springboot哪个好?
我的答案是:看场景。
| 维度 | Django (Python) | Springboot (Java) |
|---|---|---|
| 启动速度 | 秒级 | 10~30秒 |
| 内存占用 | 100~300MB | 500MB~2GB |
| 开发效率 | 极高(内置Admin、ORM) | 中(需集成大量starter) |
| 并发模型 | 多进程(Gunicorn) | 多线程(Tomcat) |
| 生态成熟度 | Web/数据科学强 | 企业级/微服务强 |
| 运维复杂度 | 低(依赖少) | 高(JVM调优、GC日志) |
对于我们这种快速交付内部工具的场景,Django完胜。但如果是高并发、强事务的金融系统,我还是会选Springboot。
最后:别怕尝试新东西
说实话,这次用Django,一开始心里是虚的——毕竟我们组Java氛围浓厚,连写个Shell脚本都要用Java重写一遍(开玩笑)。但做完发现:技术栈只是工具,解决问题才是目的。
而且,最近我在学AI,Python生态天然友好。说不定哪天,这个Django网站就能集成个LLM做智能测试用例生成?想想就兴奋。
所以,别被团队技术栈绑架。该用Python时就用Python,该用Go时就用Go。工程师的价值,不在于你会多少框架,而在于你能多快把需求变成可用的系统。
对了,那个内部工具,周三准时上线了。产品说:“界面有点土,但能用就行。”
我回他:“要不你来写CSS?”
他立刻闭嘴了 😎
附:我的VSCode Django开发插件清单
- Python(微软官方)
- Pylance(智能提示)
- Django(语法高亮)
- Django Snippets(代码片段)
- Python Docstring Generator(自动生成文档字符串)
- Better Comments(高亮TODO/FIXME)
这些插件让我少写了至少30%的样板代码。如果你也在用VSCode写Django,强烈推荐!
好了,今天就水到这里。下次可能聊聊“如何用Docker一键部署Django + PostgreSQL + Redis”,或者“Django性能调优的10个冷技巧”。
毕竟,DevOps的尽头,是自动化一切。

评论 0