ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Django技术栈实战复盘:从ORM到部署的完整链路

Django技术栈实战复盘:从ORM到部署的完整链路 聊Django技术栈之前先说一个我见过最多的误区很多人把“Django技术栈”理解成“Django这一个框架”于是学完教程就开始搭博客、写投票应用等项目真要做上线时才发现缺数据库设计规范、缺缓存方案、缺部署流程、缺权限模型整条链路全是窟窿。Django从来不是一个孤立的东西它只是整条技术栈里最核心的“Web层”而已。这篇内容不是什么入门概览而是我做了多年Django项目之后对“技术栈全景”的一次完整复盘从Django在整套系统里的定位到ORM查询与删除对象的核心写法再到和小程序uni-app、桌面端Electron、甚至AGV这类工业场景怎么配合最后给新手一条能落地的实战路线。不管你是准备用Django做后端、做全栈还是已经在用Django但想系统性理一遍技术选型这篇应该都能给你一些不算过时的参考。1. 先厘清一件事Django在技术栈里的真实位置1.1 全家桶的真面目不是框架是整套Web解决方案Django官方对自己的定义是“high-level Python web framework”鼓励快速开发和干净的设计。但真正用熟的人都知道它比“框架”这两个字的通常含义要大得多ORM、Admin后台、认证系统、表单处理、模板引擎、信号机制、中间件、国际化和迁移机制全都内置了。别的语言里这些能力往往要靠拼凑多个库完成Django则直接给你一整套这种设计导致一个很实际的结果技术栈的决策点不在框架本身而在框架之外的那圈配套组件。所以我一直把Django看成一个“有主见的全栈底座”。它的MTV架构Model-Template-View天然鼓励按数据模型、页面逻辑、展示模板来组织代码这个约束是好事也是坏事——好处是哪怕团队水平参差写出来的项目结构至少是可控的坏处是如果你硬把它当成纯API后端用就得主动放弃一部分内置能力比如模板渲染这时Django的优势会打折但ORM和Admin依然值得留着。做技术选型之前你得先搞清楚自己到底想要“全栈一体”还是“纯API服务”这决定了后面所有配套组件的选择。1.2 一套生产级Django技术栈的标配组件一个真正要上线的Django项目技术栈通常长这样Python环境管理pyenv加Poetry或uv锁定Python版本和依赖版本数据库项目早期用SQLite没问题生产基本是PostgreSQLMySQL也行但要注意utf8mb4和事务隔离级别缓存Redis几乎是标配session、缓存页面、限流都能用异步任务Celery加Redis作为broker用来处理发邮件、生成报表、调用第三方接口搜索小项目用数据库like就够了数据量上来再考虑Meilisearch或ElasticsearchWeb服务器开发用runserver生产用Gunicorn同步或Uvicorn异步前面再挂Nginx监控与日志Sentry接错误上报日志走结构化输出集中式日志系统按需上容器化Docker Compose管一套本地环境CI/CD用GitLab CI或GitHub Actions。这些不是一个新手第一天就要全学会但你应该知道完整的技术栈地图长什么样。比如我在实际项目里最常见的组合就是PostgreSQL加Redis加Celery再加Nginx整套下来能扛住绝大多数中小型业务的负载。真正需要微服务、Kubernetes的阶段离绝大多数团队还很远别提前给自己加戏。1.3 Django与uni-app、Electron、AGV技术栈没有“最好”只有“配合”这几年我经常被问到“Django和uni-app怎么选”“Django和Electron是不是重复了”。这类问题本质上没有分清层级Django是后端服务uni-app是跨端小程序框架Electron是桌面应用容器它们根本不是替代关系而是上下游配合关系。uni-app技术栈解决的是“一套代码编译到微信小程序、H5、App”的问题前端用Vue语法后端接口往往就是Django搭的DRF服务。Electron技术栈解决的是“用Web技术做桌面端”的问题后端既可以走远程API也可以在本地内嵌一个Django服务做纯本地数据处理。至于AGV自动导引车这类工业场景技术栈里通常有嵌入式端STM32、ROS、通讯层MQTT、Modbus、调度上位机C#、Java而后台的Web管理端和实时监控大屏用Django加DRF加WebSocket来做一点不违和。技术栈不是单选题关键是你得知道每一层该放什么。2. 从创建app到工程化把Django项目搭成能长期维护的样子2.1 创建项目与app的正确姿势Django创建项目有两级结构project项目和app应用。新手经常搞混的是这两个东西到底什么关系。简单说project是整套服务的配置容器app是具体功能模块比如一个电商网站可能有orders、products、accounts三个app。创建命令本身很简单django-admin startproject myproject python manage.py startapp accounts但有几个细节值得注意。第一project名称不要用带连字符的名字因为后面import的时候会出问题用下划线或者直接用驼峰。第二app名称习惯上用复数名词比如orders而不是order这是一个约定俗成但不强制的好习惯能让代码读起来更自然。第三app创建后别忘记加到settings的INSTALLED_APPS里这大概是被问烂了却又永远会发生的低级错误。我在新的项目里通常会先建好“core”这个app放自定义User模型、基础Model抽象基类、通用的中间件和工具函数这样后面每个业务app都能从公共底座继承避免重复造轮子。2.2 settings拆分与配置管理Django项目的配置全都在settings.py里但那种“一个settings.py走天下”的写法我只建议用在玩具项目里。稍微像样一点的团队项目至少要把配置拆成base、dev、prod三个文件base放公共部分dev放DEBUG和本地数据库prod放生产配置和关闭DEBUG后的白名单。再配合环境变量管理敏感信息比如SECRET_KEY、数据库密码、第三方API密钥一律不进代码仓库。我习惯用django-environ或pydantic-settings来读环境变量.env文件只存在于本地和部署机不提交到Git。还有个容易被忽视的配置是ALLOWED_HOSTS开发环境可以留空但生产环境必须明确列出你的域名否则Django会拒绝任何不带正确Host头的请求。以及TIME_ZONE我踩过不少坑之后现在的统一做法是全局设置为UTC在展示层再转本地时区避免夏令时和业务计算混乱。拆分配置还有一个目的任何人clone代码后只需要复制一份.env.example为.env填上自己的本地配置就能跑起来。这比在README里写“请改settings.py第几行”要可靠得多也是团队协作里比写文档更高效的一种“自解释”设计。2.3 目录组织与代码分层Django官方教程并没有告诉你一个复杂的业务项目该怎么组织目录新手往往写着写着就把所有逻辑堆进views.py一个文件上千行。这里分享一套我经过多次重构后沉淀下来的分层方式项目根目录下统一放一个apps目录所有业务app都放进去而不是散落在manage.py旁边每个app内部至少区分views、models、serializers如果用了DRF、services、urls五个文件services层专门放核心业务逻辑避免view变得臃肿公共的模型基类统一放在core app里比如带idUUID主键更佳、created_at、updated_at的基础模型自定义工具函数集中在utils或common模块跨app通用。这套结构的核心逻辑是“按领域垂直切分”而不是“按技术角色水平切分”。换句话说订单相关的东西都放进orders这个目录里而不是把所有view放一起、所有model放一起。这样每个app都是相对独立的小系统边界清楚后续维护和交接都轻松不少。3. ORM核心实战查询与删除对象这两关过了基本就出师3.1 查询对象QuerySet的惰性、N1与三种必会写法Django ORM查询对象本质上是操作QuerySet。新手最容易忽略的一点是QuerySet是惰性的创建它并不会立刻执行数据库查询只有真正取数据、遍历、判断布尔值、切片的时候才会触达数据库。由于这个特性很多人会写出看似高效实则很糟糕的查询代码。典型问题是N1查询。比如你要展示一个订单列表每条订单要显示对应的用户昵称初级写法可能是在循环里再查一次user表orders Order.objects.all() for order in orders: print(order.user.username) # 每循环一次就多一条SQL一旦订单数量上来数据库直接被拖垮。正确做法是使用select_related适合一对一、外键或prefetch_related适合多对多、反向关联一次性把关联数据查出来orders Order.objects.select_related(user).all()另外两个必须掌握的查询写法是Q对象和F对象。Q对象用来组合复杂查询条件比如“状态为已支付或支付时间在最近一小时”from django.db.models import Q orders Order.objects.filter(Q(statuspaid) | Q(paid_at__gtone_hour_ago))F对象则用于字段与字段之间的比较或在原值基础上更新典型场景是商品库存扣减from django.db.models import F Product.objects.filter(pkproduct_id).update(stockF(stock) - 1)用F对象更新能保证原子性避免“读出来、减一、再写回去”这个流程里出现并发超卖的问题。这些都是Django执行查询时最常碰到的核心套路比背API列表管用得多。3.2 删除对象级联、软删除与批量删除的坑Django删除对象常见的方式有三种单个实例的delete()、QuerySet的delete()、以及物理删除之外“看起来像删除”的软删除。先说前两种很多人会踩的坑是删一个对象可能连带着删掉一堆关联数据。这取决于外键的on_delete参数CASCADE表示级联删除PROTECT表示有引用时拒绝删除SET_NULL表示外键置空。刚做项目时我对所有外键无脑用CASCADE结果删一个用户把订单、日志、关联文件全删没了。后来做金融类项目时连CASCADE都很少用基本都改成PROTECT或SET_NULL主动在业务层做清理宁可多写代码也不让数据库替你“冲动”。还要注意一个容易出问题的细节Model.delete()和QuerySet.delete()的行为并不一致。Model实例的delete()会触发数据库级联和信号而QuerySet.delete()是直接SQL批量删除默认不会触发Model里定义的delete方法也不会逐条发送信号。如果你依赖信号来做审计日志或同步操作批量删除时会发现那些钩子根本没执行。所以如果业务强依赖信号更安全的做法是循环遍历逐个删除或者显式在批量删除后手动补齐审计逻辑。软删除是另一个值得早点重视的设计。所谓软删除就是给模型加一个is_deleted布尔字段删除时并不真正DELETE而是把is_deleted置为True查询时默认过滤掉。好处是数据可恢复、保留历史记录、不会破坏关联数据代价是每张表都要处理过滤逻辑以及唯一性约束要考虑到已删除数据。我现在的习惯是核心业务表、涉及资金和用户数据的表一律软删除日志表、临时表这类无关紧要的可以用硬删除。选型原则很简单——看这条数据丢了之后你愿不愿意花钱花时间找回来。3.3 事务与并发查询删除之外的底线查询和删除是每天写得最多的操作但真正考验一个Django开发者水平的是对事务和并发的理解。最典型的需求是转账扣A账户的钱、加B账户的钱两步必须一起成功或一起失败不能中间断电。from django.db import transaction with transaction.atomic(): account_a.balance - amount account_b.balance amount account_a.save() account_b.save()transaction.atomic保证块内操作在一个事务里任一步出错就整体回滚。还有个配套技能是select_for_update它在事务内对选中的行加锁防止两个请求同时修改同一行数据with transaction.atomic(): account Account.objects.select_for_update().get(pkaccount_id) account.balance - amount account.save()这个写法对高并发场景极其重要比如秒杀扣库存、拼团报名这种临界区逻辑。我有段时间就是因为没加select_for_update导致线上库存被多扣了排查半天才发现是并发覆盖。道理很简单先读出来改再写回去天然存在竞态条件你不锁行数据库就会替你“分胜负”但结果往往不是你期望的。4. 前端搭配怎么选Django Templates、DRF、uniapp小程序与Electron4.1 传统全栈Templates Bootstrap内部系统首选如果你做的是公司内部管理系统、数据后台、运维平台这类工具用户少、逻辑复杂、对界面要求不高那我建议直接用Django Templates加Bootstrap别折腾前后端分离。这一代的Django模板能力其实不弱配合django-bootstrap5、htmx这类轻量库能做出交互相当流畅的界面而且没有跨域、Token刷新、接口文档一堆麻烦事。我接过不少“本来很简单却被前后端分离搞复杂”的项目一个内部报表系统团队硬上了Vue加DRF加JWT结果光联调就花了两周。如果用模板渲染可能两天就交付了。技术选型不是越新越好而是要匹配场景复杂度。模板方案的另一个优势是Django Admin可以直接当后台用很多管理展示场景完全不需要自己写页面。4.2 前后端分离DRF Vue/React 是目前主流对外提供服务、小程序、App后端以及需要多端复用的场景基本都会走前后端分离。Django这边几乎默认的选择是Django REST FrameworkDRF它把序列化、视图集、路由、认证、权限、分页、限流都封装好了。技术栈大概是“DRF djangorestframework-simplejwt django-cors-headers Django Filter Swagger/OpenAPI文档”。这套组合的威力在于你只需要定义好序列化器和视图集路由注册自动生成文档自动生成前端直接从接口文档拿数据。和我配合过的Vue/React团队都反馈这套流程比之前用Node后端还顺畅因为DRF的序列化和权限体系太完整了。但也要当心别让DRF替你做太多极端复杂的聚合查询、报表统计直接在ORM层硬拼很容易写出慢SQL这种场景我一般会单独写原生SQL或落到物化视图里再通过DRF暴露出去。4.3 uniapp小程序后端一个实战可复用的协议设计“用uniapp做小程序用到的技术栈”是热搜词里被问得特别多的因为很多前端转小程序的人后端会下意识选择Django。这份搭配确实成熟uniapp前端只管Vue语法的页面和请求封装Django后端用DRF提供JSON接口。小程序和普通Web后端最不同的地方是登录态。微信小程序要先用wx.login获取code后端拿到code后调用微信接口换取openid再用openid找到或创建本地用户最后签发Token。这个流程一定要做成独立模块因为它涉及密钥管理和网络请求不能散落在业务代码里。我画过很多次这个图小程序前端传code给后端后端换openid再返回JWT之后所有请求都带JWT。这套流程一旦跑通后面无论加多少业务接口都顺了。另一个小程序特有的坑是支付回调。微信支付成功后是异步通知你的服务器通知里带着签名后端必须验签再更新订单状态千万别信前端传回来的“支付成功”。这个提醒我说一万遍都不嫌多因为真有团队为了赶进度直接信任了前端状态最后对账对不上。4.4 Electron桌面端与Django本地组合Electron做桌面端Django做本地服务这个组合在AI工具类产品里越来越常见桌面端UI用Electron本地启一个Django服务处理数据计算、文件解析、调用模型然后通过localhost接口和前端通信。这样做的好处是能用Python生态处理重活又能用Web技术做界面。这种模式下技术栈的注意点不太一样Django不需要跑在生产级服务器上runserver已经是够大概率但要在Electron应用里把它作为子进程拉起退出时一起回收同时处理端口冲突和日志清理。另一个常见坑是跨域和CSPElectron渲染进程访问localhost接口时如果没配好会不停报错我通常会让Django监听127.0.0.1然后用一个固定的本机端口并在Electron侧进行白名单放行。还有打包Python端用PyInstaller打包成独立可执行文件再放进Electron应用的resources目录这样用户装完就是一个完整的桌面软件不需要提前装Python。5. 新手实战路线图从Demo到上线这五步照做不会乱5.1 一条验证过的新手学习路径被问到最多的问题永远是“Django项目实战新手该怎么做”。我见过太多人买了课、敲完教程代码、然后就卡住了——因为教程项目是别人设计好的没有教你怎么从需求出发做决策。我总结了一条对新手比较友好的学习路径按顺序走不会乱第一步搭建环境。装Python 3.10以上、Poetry、Docker Desktop把项目跑起来。第二步克隆一个真实的小项目练手比如个人记账、团队周报这比博客和投票更有业务感。第三步按我上面说的拆分配置和目录组织方式重构一遍哪怕代码写得不完美也要养成结构化的习惯。第四步加上用户登录、权限控制、信号量、事务这些“看起来高级”的能力这里才是Django和脚本本质区别的分水岭。第五步把项目部署上线哪怕只是用一台服务器跑起来也要完整走一遍迁移、静态文件、日志、备份的流程。这五步每一步都不快但走完以后你就已经具备独立做一个小项目的完整闭环能力了。不要一上来就去学分布式、微服务、消息队列那些东西在没有真实流量压力的时候只是负担。5.2 部署的黄金组合与上线检查清单Django部署的方案很多我长期稳定使用的组合是“Nginx Gunicorn PostgreSQL Redis Docker Compose”。Gunicorn跑Django应用Nginx做反向代理和静态文件服务PostgreSQL存数据Redis管缓存和SessionCompose把一整组服务用一条命令拉起来services: web: build: . command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 volumes: - static_data:/app/staticfiles depends_on: - db - redis nginx: image: nginx:latest volumes: - static_data:/static ports: - 80:80 depends_on: - web db: image: postgres:16 env_file: .env redis: image: redis:7上线前一定要过一遍检查清单DEBUGFalse、ALLOWED_HOSTS配好、SECRET_KEY从环境变量读取、执行collectstatic把静态文件集中到一处、数据库迁移完成、日志有落盘、监控告警至少有一个错误上报通道。这些普通步骤虽然是挂在嘴边的但真正每条都做到的项目我见到的比例不高。5.3 性能体检先解决N1再谈优化很多新手做完项目上线后会突然被告知“页面加载很慢”。不要急着上缓存第一步永远是先查SQL执行情况。装上django-debug-toolbar或者用django-silk记录请求期间的SQL一眼就能看到有没有几十条相同的查询飘着。出现这种现象十有八九是N1先把select_related和prefetch_related补齐这类优化往往能直接让接口耗时下降一个数量级。第二步才是上缓存模板片段缓存、Django缓存框架接Redis、数据库查询缓存。第三步才是考虑异步化比如报表导出这类耗时操作丢给Celery。很多团队搞反了顺序项目还不到1000QPS就先搭一套缓存集群结果最基础的SQL全表扫描还在。记住这个优先级查得少比缓存快更重要缓存快比机器多更重要。6. 用AI Agent开发Django能提效但边界必须清楚6.1 AI辅助Django开发的四种模式“用AI Agent开发Django”是这段时间讨论度特别高的话题我实际用下来的感受是AI确实能大幅提速但效果取决于你把它放在哪个环节。目前常见的模式有四种。第一种是补全式在IDE里用GitHub Copilot或Cursor写函数注释它自动生成代码适合CRUD和样板代码。第二种是对话式把需求描述给AI让它生成模块代码然后你review之后粘贴进项目适合不熟悉的API查询、配置片段。第三种是Agent式AI根据你的任务自动规划步骤、创建文件、安装依赖、跑测试并迭代适合“从零搭一个CRUD服务”这种目标明确、验收标准清晰的任务。第四种是人工兜底式AI负责生成候选实现你负责测试和排错这是我最推荐的协作方式。6.2 AI生成Django代码的高频错误与对策我对AI生成Django代码持开放态度但绝不无脑接受。实际踩过并且反复出现在AI输出里的错误我整理成了下面这个速查表常见错误典型表现对策过时API使用了老版本Django的写法比如已废弃的ugettext明确告诉AI你用的Django版本或复制文档片段给它重复路由每个视图都自己声明URL和DefaultRouter冲突要求AI优先使用DRF的ViewSet加Router事务边界过宽整段逻辑包在一个transaction.atomic里长事务锁表人工拆分事务范围只把关键操作包进去忽略批量删除信号用QuerySet.delete()导致信号不触发提醒AI注意Model.delete和QuerySet.delete的区别时区处理错误用datetime.now()而不是django.utils.timezone.now()在项目规范里规定统一用timezone.now()忘记分页列表接口直接返回全部数据明确要求分页类以及page_size这些坑其实不是AI独有的新手也会犯但AI出错更快因为你可能review得不够仔细。我的习惯是任何由AI生成的代码必须经过项目里的测试跑一遍重点看模型迁移、测试用例、接口返回三个环节。6.3 我的AI协作工作流现在我在实际项目里的AI协作方式大概是这样先自己把需求和边界想清楚画出数据模型和接口列表然后把模型定义和需求描述交给AI生成初始代码接着我人工把settings、迁移、权限这块重写一遍因为这是整个项目的地基最后让AI根据测试结果迭代修bug。这套流程里AI负责体力活我负责方向和质量。写过“django创建app”的人会有体感startapp本身已经把骨架生成好了AI更大的价值在后半程比如根据模型自动生成序列化器、接口文档、基础测试。但权限模型、支付回调验签、资金流水这类逻辑我到现在还是坚持自己写AI生成的我至少要找其他同事review三轮才敢合入。这不是对AI不信任而是这类逻辑一旦出错代价远大于“省下的那几个小时”。我个人在实际操作中的体会是Django项目做久了你会发现框架本身能坑你的地方其实很少真正决定项目生死的全在工程细节数据模型有没有设计好查询有没有避开N1删除行为有没有想清楚级联还是软删事务边界划得对不对部署流程能不能一键复现。所谓资深的Django开发者强在每一条细节都踩过坑、都形成了自己的应对方案。最后再分享一个小技巧把项目的所有运行配置数据库、缓存、队列、Nginx固化到Docker Compose里新同事入职或者自己电脑出问题时一条命令就能还原整套环境。这个习惯我坚持了好几年每次觉得麻烦的时候都庆幸自己当时没有偷懒。
返回列表