ARTICLE DETAIL

资讯详情

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

Django博客开发全流程:从ORM模型设计到部署避坑指南

Django博客开发全流程:从ORM模型设计到部署避坑指南 最近有个新入行的朋友问我想学Django但不知道从哪个项目入手看了一堆教程还是写不出来。我给他的建议始终是同一个——写一个博客系统。这不是敷衍Django全栈开发最典型、最有完整闭环的项目就是博客它有增删改查、有用户登录、有列表页和详情页、有表关联甚至能直接推到线上。这篇文章就是我把从零搭博客的过程完整梳理了一遍从设计表结构讲到登录权限、从ORM查询讲到部署前要避的坑。目标是让读者看完之后能照着敲出一个能跑、能写文章、能登录注册的博客系统。适合刚学完Python基础、想用Django做第一个完整项目的朋友也适合已经写过一两遍但想理清全栈脉络的同行。1. 项目定位与技术选型为什么博客是Django入门的教科书式项目1.1 博客系统恰好覆盖Django的核心能力很多人选第一个项目时要么选一个“待办事项列表”要么选一个“图书管理系统”。这类项目不是不能练而是练完之后你会发现自己只是学会了“抄代码”。Django的核心优势不在于“快”而在于它把Web开发里大量重复、容易出错的部分做成了规范化的框架。博客系统恰恰能踩中Django绝大多数功能点。文章是典型的CRUD资源。列表页、详情页、创建页、编辑页、删除确认页这五种页面构成了Web应用最通用的骨架任何系统几乎都逃不开这一套。博客之外像CMS内容管理、企业官网的后台、甚至一些简单电商后台都是这个模式。更关键的是博客天然需要多张表协同工作。文章有作者、有分类、有标签这三个字段分别对应一对一、外键和多对多关系。很多新手一开始只写一张简单的表以为ORM就是“给一个类加几个字段”完全没体会到关系型数据库的设计逻辑。博客恰恰能让这三种关系在一个项目里全部出现并且每个关系都有明确业务含义。这一点是“待办事项列表”给不了的。另外一个容易被忽略的好处是博客的前端不会太复杂。Bootstrap加模板继承就够用了不需要提前学一个独立的前端框架。这意味着Django的模板系统、静态文件处理、表单渲染这些后端工程师必须懂的前后端协作知识可以在博客项目里完整走通。博客作为入门项目不是因为简单而是因为它的复杂度刚好卡在“能练全流程”和“不会劝退”之间。1.2 功能边界怎么划先做MVP别给自己加戏我写这个博客的时候第一步不是开项目而是写了一个功能清单。刚开始我也犯过错想加搜索、想加点赞、想加RSS订阅、想加暗色模式最后全部忍住了。最终保留下来的规划是用户认证注册、登录、登出以及基于登录状态的权限控制。文章管理发表、编辑、删除自己的文章未登录用户只能看。分类与标签文章归类列表页可以按分类筛选。评论功能登录用户可以发表评论并关联到具体文章。这里尤其是“点赞”和“搜索”我建议新手第一版别碰。点赞涉及反作弊、去重和数据统计往深了说会牵扯Redis、消息队列甚至前端交互的状态管理远超入门范畴。搜索更明显如果刚上来就接Elasticsearch光是环境搭建和索引同步就够喝一壶。Django自带的filter做简单包含搜索足以支撑个人博客的阶段。在我后来的实际体会里这四个核心模块做完整个全栈开发的骨架已经非常完整。后续想加任何功能都是往这个骨架上填肉不需要推倒重来。项目边界划得清楚开发过程中才不会三天两头改需求、改模型把学习精力浪费在无效返工上。2. 数据模型设计博客的地基怎么打才稳2.1 三张核心表的设计逻辑动手写代码前我会建议先在纸上画一下表关系。文章表、分类表、标签表是博客的数据核心我逐个说说当时的设计思路。先看文章表。常规字段无非是标题、正文、摘要、封面图、创建时间、更新时间。有两个字段值得单独讲我建议每个新手都要认真理解第一个是status。我把它设成IntegerField0代表草稿、1代表已发布。为什么不用BooleanField因为状态这种东西以后大概率会扩展例如增加“审核中”“已下线”“定时发布”等状态。整数字段从0开始递增扩展性远强于布尔值。发布状态直接决定了前端查询时用不用filter过滤。写文章不可能一次写完草稿功能非常实用。第二个是slug。它用来做文章URL的优化。Django默认主键是自增id如果URL是/blog/3/这样能用但不好看对SEO也不友好。用slug把标题转成hello-django这样的标识符URL就变成/blog/hello-django/了。slug字段在创建文章时可以由标题自动生成Django自带slugify()函数可以轻松实现。分类表很简单名字、slug。标签表同样。这里有一个关键点需要认真理解关系方向。一篇文章只能属于一个分类所以文章表里放的是ForeignKey一个分类下可以有很多篇文章反向查询就是分类下的文章列表。文章和标签之间是ManyToManyField一篇文章可以有多个标签一个标签也可以对应多篇文章。这也是博客系统跟普通学生管理系统的本质差异它不是单表或多张孤立表而是通过关系保证业务数据不冗余、不乱套。至于ORM对多对多的处理方式很多人会懵。你迁移后在数据库里找不到文章的tags字段但能看到一张名叫类似blog_article_tags的中间表。这张表就是Django自动生成的关系表里面只存文章ID和标签ID的对应关系。如果你想在新增文章时顺便把标签也创建好那么就要注意多对多关系的add和create用法一块儿处理。2.2 用户权限与登录功能的底层思考博客必须解决“谁能改谁的文章”这个问题。Django自带的User模型已经覆盖了用户名、密码、邮箱、权限组等绝大多数场景。我的原则是能用内置就别自定义。用第三方包当然快但学习阶段亲手做一遍登录链路比什么都强。登录功能的实现逻辑是这样的前端提交用户名密码Django用authenticate()验证验证成功后调用login()写入会话。后续请求带着session或者cookie去访问框架里的request.user就能自动指向当前已登录用户。这段链路里最核心的词是“session”本质上服务端存了一份记录了当前登录状态的钥匙浏览器把钥匙存在cookie里每次请求带上就行。理解了session机制登录功能在你眼里就不再是一个黑盒。权限控制分两层。第一层是登录要求用login_required装饰器包住发布、编辑、删除这几个视图未登录用户访问这些URL会自动跳到登录页。第二层是对象级权限文章模型上有author外键编辑或删除前先判断request.user article.author不是作者就直接403。这里有个常见的坑很多教程只做了登录判断忘了判断“登录了不代表是文章作者”结果就是任何注册用户都能改别人文章这在实战里属于严重的数据安全问题。我在帮别人review代码时见过不少次值得专门拿出来强调一遍。3. 从空目录到能跑的项目全流程实操记录3.1 创建项目与应用的一些技巧写代码之前先交代环境。我用的是Python 3.10和Django 4.x两个版本都比较稳定。创建项目的过程我直接列命令mkdir blog_project cd blog_project python3 -m venv venv source venv/bin/activate pip install django django-admin startproject config . python manage.py startapp blog python manage.py startapp accounts这里有个小技巧为什么项目用户目录叫config而不是blog如果你直接startproject blog那主目录名就是blog再startapp blog就会撞名后续import时容易出各种莫名其妙的问题。我把项目配置文件目录命名为config应用命名为blog职责就清晰了。config是拿着方向盘的那双手blog是真正干活的发动机。startapp之后立刻去settings.py里的INSTALLED_APPS把两个应用加进去。这一步漏了后面models建了表也不会生效。这个坑可以说是新手高频问题榜首我见过太多人问“为什么我定义了模型但迁移提示找不到”。你如果看到这句话先回去看看INSTALLED_APPS十有八九就能自己解决。3.2 settings配置与数据库选择的取舍项目创建完成后默认语言是英文、时区是UTC对中文博客来说肯定要改。settings.py里最关键的几行LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ TrueUSE_TZ这个配置值得多说几句。Django官方推荐True意味着数据库里存的是UTC时间页面展示时才转换成当地时间。很多新手贪图省事把USE_TZ改成False结果在时间计算、按日期分组统计时遇到各种错乱。我的观点是保持True不动。模板里Django会自动适配时区后端代码需要手动转换时用localtime方法几乎没有额外负担。时间处理的坑是最隐蔽的你踩过一次就会明白官方为什么默认推荐开启。数据库方面入门阶段用默认的SQLite完全够。不急着上PostgreSQL或者MySQL因为你的核心任务是学Django本身没必要把数据库的坑提前搬过来。数据库切换不需要改逻辑代码以后要换时把ENGINE换成django.db.backends.postgresql再补几个连接参数就搞定。3.3 迁移、ORM查询与删除对象的踩坑记录定义好模型之后迁移命令是每个Django项目都躲不开的门槛python manage.py makemigrations python manage.py migratemakemigrations是把模型类翻译成数据库操作脚本migrate才是真正执行脚本去建表。这条命令背后有个极具价值的小技巧makemigrations之后用python manage.py migrate --plan可以提前查看将要执行的SQL操作用python manage.py sqlmigrate blog 0001还能直接看到Django生成的原始SQL语句。面试官如果问“Django迁移是怎么管理的”你能说出这三条命令的差异就能证明你不是只会照抄命令跑流程。ORM查询方面我整理一份最常用的写法对照查所有文章Article.objects.all()返回QuerySet按发布时间倒序Article.objects.order_by(-created_at)只查已发布Article.objects.filter(status1)查单个Article.objects.get(id1)注意查不到或查重都会抛异常创建Article.objects.create(title..., content...)更新先取对象改字段再save()或者批量update()删除article.delete()热词里提到“django执行查询-删除对象”关键点正是delete()。这个方法有返回值返回的是(总删除数, 各类型删除数)。这个返回值在外键关系存在时尤其值得注意因为一对一、外键、多对多都会触发级联删除。多对多关系下的部分删除行为是新手最怕的。我曾经吃过一次亏删一个分类时把这个分类下所有文章都删了。后来才意识到想要保留文章、仅仅删除分类就该在ForeignKey上设置on_deletemodels.SET_NULL同时让该字段允许nullTrue。on_delete这个参数建议每个新手花十分钟搞清楚五个选项的区别后面是否发生“删一个边角料毁掉整棵树”的惨案全看它。3.4 登录路由与发表文章的完整链路登录功能的完整链路我建议亲手写别装一堆第三方认证库。以我的经验核心视图写出来长了也就二三十行from django.contrib.auth import authenticate, login def user_login(request): if request.method POST: form LoginForm(request.POST) if form.is_valid(): username form.cleaned_data[username] password form.cleaned_data[password] user authenticate(request, usernameusername, passwordpassword) if user is not None: login(request, user) return redirect(blog:article_list) else: form LoginForm() return render(request, accounts/login.html, {form: form})写完登录后最自然接着做的事情就是“创建文章”。创建文章视图基本逃不开request.method判分支的套路GET返回空表单POST校验表单、保存、跳转详情页。保存时有一个容易忽略的细节文章需要自动关联到当前登录用户。所以新建时不能直接ModelForm.save()要save(commitFalse)先把表单数据变成模型对象手动给author属性赋值再二次save()落库。我特意提一下commitFalse这一步是因为它代表了Django表单系统里一个非常核心的概念把表单与模型解耦。理解了这个参数再去看Django的ModelForm源码整个表单工作流会豁然开朗。模板层面建立一个base.html作为骨架里面用{% block content %}{% endblock %}掏出内容区所有页面extends它。再引入Bootstrap的CDN几分钟就能从“纯文本页面”变成“有导航栏的网页”。模板里判断登录状态用的是{% if user.is_authenticated %}已登录显示用户名和退出按钮未登录显示登录链接。这些语法是新手最容易卡壳的小点因为它们是模板语言而不是Python调试的时候找半天可能只是少写了一个endblock。我想说的是模板错误信息往往不像Python那样给出清晰的行号所以写模板时务必把endblock这种闭合标签当作和中括号、引号一样重要的语法处理。3.5 Django Admin被低估的全栈利器很多全栈教程忽视了Admin但我认为它是新手理解Django威力的最佳窗口。只需在admin.py里写几行注册代码from django.contrib import admin from .models import Article, Category, Tag admin.site.register(Article) admin.site.register(Category) admin.site.register(Tag)然后运行python manage.py createsuperuser创建管理员账号访问/admin/就能直接在后台对文章做增删改查管理分类标签上传封面图全程可视化。对个人博客来说这个后台甚至可以完全当运营管理端使用不需要再单独写一套后台界面。Admin真正强大的地方不是registr而是自定义。list_display控制列表显示哪些字段list_filter提供侧边栏筛选search_fields配置搜索框fieldsets可以按区块组织编辑表单。这套自定义能力掌握之后很多企业内部工具的管理界面都能靠它直接交付省掉大量重复的前端工作。4. 开发调试期绕不开的坑问题排查与避坑指南4.1 静态文件加载失败这是Django新手遇到的第一个大坑我把它放第一位。开发阶段settings.py里一般需要手动配好STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]配了还是不显示的话第一步用浏览器DevTools看Network里静态资源的请求URL是多少确认路径第二步看模板里有没有写{% load static %}。常见错误无非两类一类是模板里直接用相对路径写死另一类是忘了在模板顶部加载static标签。这两个错误说起来都简单但你如果没有排查经验可能对着屏幕干瞪半小时。我后来养成的习惯是配完settings先写一个只有一张图片的测试页确认静态文件通路正常后再往下写布局能省非常多无效调试时间。4.2 CSRF验证失败与登录跳转问题Django的表单POST请求默认都要带CSRF token模板里忘了写{% csrf_token %}提交时就会看到红色403错误页。这其实不是Bug而是安全机制。Django要求每个POST请求携带一个由服务端生成的随机token用来防御跨站请求伪造攻击。理解了这一点处理起403就不会烦躁。至于登录跳转失效十之八九和LOGIN_URL或next参数配置有关。在settings里显式设置LOGIN_URL /accounts/login/凡是未登录的请求被驳回后Django会记住原来要访问的地址拼在next参数里登录成功后再带用户回原页面。这个体验闭环对博客来说很重要。设想一下读者看完文章想写评论被要求登录登录完又找不到刚才的文章了这体验该多差。4.3 查询性能与N1问题博客首页列表页要展示文章和分类很多人直观的写法是循环里点属性取值比如article.category.name。如果一页显示10篇文章就会额外触发10条查询。这种就是典型的N1问题很多Django项目上线后变慢的元凶都在这里。解决方式是用select_related和prefetch_relatedarticles Article.objects.filter(status1).select_related(category).prefetch_related(tags)select_related用于外键它底层是SQL JOIN把关联表数据一次取回来。prefetch_related用于多对多底层是第二次批量查询后在内存里做关联。这个优化在开发环境数据量小的时候根本看不出来等文章超过几百篇后响应时间差异会非常显著。我建议Django新手从第一天就养成一个习惯在列表页开django-debug-toolbar盯着SQL查询次数看这比任何性能教程都更直接。4.4 迁移文件冲突与意外数据丢失开发过程改模型是常态改完一定记得重新makemigrations。我遇到最诡异的场景是不同环境下各自动过迁移生成了编号重叠的文件导致migrate时报“检测到多个叶子节点”或者直接提示冲突。这种情况下可以执行python manage.py migrate app_name --plan手动规划合并点也可以把冲突迁移里的改动在模型里补齐后删掉多余迁移文件用makemigrations重新生成。需要特别注意一点线上已经迁移过的数据库绝对不能直接删除迁移文件否则Django会视为表不存在处理起来相当痛苦。我在本地开发时吃过一次苦头为了“干净”把migrations目录全删了重新生成结果数据库里的django_migrations记录对不上最后只能重建库。现在我的习惯是迁移文件只增不改、不随意删除这个原则能帮你避免大量数据层面的折腾。5. 部署前收尾让项目具备上线体质的几点建议5.1 拆分开发与生产配置开发阶段把一切配置写进一个settings.py没问题但项目一旦准备部署第一件要做的事是拆分配置。我推荐的做法是把settings.py改成settings/包里面放base.py、dev.py、prod.py通过环境变量控制加载哪套。理由很简单你不可能一直开着DEBUG来记录异常也不可能在线上暴露本地路径和相关密钥。虽然对一个入门项目来说这显得有点“工程化过度”但如果你以后想靠Django吃饭这个习惯越早养成越好。5.2 用环境变量管理秘密信息SECRET_KEY、数据库密码、第三方API key绝不写死在代码里。用django-environ或者干脆用os.environ读取。我的习惯是项目根目录放一个.env文件里面存所有真实配置同时把.env写进.gitignore防止意外提交。这事不是小题大做我看到过太多直接把SECRET_KEY提交到公开仓库的案例被爬虫扫到之后整个会话体系都不可信只能紧急轮换密钥。5.3 静态文件和Admin样式的托管开发模式里Django能自己处理静态文件但生产环境用Nginx加Gunicorn跑的时候必须通过python manage.py collectstatic把散落在各app的静态文件收集到统一目录交给Nginx托管。这个命令理解起来很简单但有个前提你需要安装whitenoise之类的库并把它加进中间件。否则Django在关闭DEBUG之后默认不会帮你托管Admin后台的样式。Admin界面如果变成纯HTML“裸奔”视觉效果就好像电脑中了病毒一样搜索框、按钮全部错位。第一次部署时如果不提前了解这点很容易以为自己哪里配错了。写到这里聊聊我个人的一些体会这个博客项目走完一遍你会发现它带给你的东西远远超出“会写一个能用的博客”本身。建模型时对关系型数据库的理解写登录时对session机制的掌握排查N1时对查询性能的意识这些都是能迁移到任何Web项目上的底层能力。我有几个经验性的建议算是送给走到这里的朋友。第一自己动手把表的关联画一遍不要直接照抄模型的代码。第二登录链路别用第三方包亲手写一次。第三开发过程中坚持开django-debug-toolbar把SQL查询次数当作健康指标看待。做到这三点你再去接触更复杂的项目时会发现Django世界里很多东西都是相通的。如果下一步想继续发展这个博客可以试着把评论改成异步通知把文章搜索换成PostgreSQL的全文检索或者给首页加上一个简单的Redis缓存。这些扩展都会让学习曲线平滑地继续向上延伸而你现在已经站在一个足够稳固的起点了。
返回列表