Django入门:从零搭个Python网站,结果被Springboot老哥们笑了?

邓文
2026-01-13 13:09
阅读 1154

说实话,作为一个干了快两年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。

切换过程其实不难:

  1. 安装psycopg2驱动
  2. 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'),
    }
}
  1. 执行迁移: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

最热最新
暂无评论
邓文Lv.1
0
影响力
0
文章
0
粉丝