Django全栈开发入门:从零构建博客系统实战指南
你是不是也想做一个自己的博客系统却不知道怎么选技术栈我的建议一直很直接想入门全栈开发就从 Django 动手。博客这个项目麻雀虽小、五脏俱全从数据库建模、ORM 查询到路由、模板渲染、后台管理、文件上传、部署上线几乎把 Django 全栈开发的核心链路完整走了一遍做完之后你会对整个 Web 开发流程有一个扎实的体感。这篇文章围绕“Django全栈开发入门构建一个博客系统”展开适合刚看完 Python 基础、想跳出教程自己动手的人也适合已经会用 Flask 这类轻量框架、但想体验全家桶框架完整度的朋友。我会把从零搭一个博客的完整思路、代码、配置、部署和踩坑过程都整理出来尽量让你跟着就能复现。1. 先拆解项目博客系统的范围与技术选型1.1 为什么是 Django而不是其他框架市面上 Python Web 框架不少Flask 灵活、FastAPI 性能好、Django 则是一套全家桶。很多人纠结怎么选我的建议是如果想系统性地理解全栈开发Django 是最合适的入门工具。原因很简单Django 自带 Admin 后台、ORM、表单处理、认证系统、静态文件管理、模板引擎这些全是 Web 开发的高频组件。用 Flask 时你可能还要自己去拼 SQLAlchemy、Jinja2、Flask-Login、Flask-Admin光选型就消耗不少精力Django 帮你把这些都打通了你只需要关注业务本身。而且 Django 的 ORM 设计得很好Model 层写好后迁移、建表、查询、关联、删除这些操作都有清晰的规范。对于入门者来说这种“官方给你标准答案”的体验非常友好你能把精力放在理解业务逻辑上而不是纠结某个库怎么配置。我用的是 Django 4.2 LTS 版本。为什么不追最新版LTSLong Term Support意味着长期维护社区资料多、坑少尤其在生产环境稳定比“新”重要得多。Python 版本我推荐 3.10 以上4.2 对 3.10/3.11 的支持都很成熟。1.2 博客系统的功能范围怎么定很多新手做项目时容易犯一个错误功能越加越多最后烂尾。我踩过这个坑所以这次做博客我一开始就明确边界只做核心闭环文章的发布、编辑、删除通过 Admin 后台管理不单独做前端管理页面文章分类与标签首页文章列表 分页文章详情页图片上传与视频上传部署上线评论区、用户注册、点赞这些先不做不是难而是它们会分散你对主线的注意力。入门项目的核心目标是跑通“数据模型 → 视图 → 模板 → 部署”这条链路而不是功能越多越好。等到这个基础版本跑通了再往上加功能就很容易。Django 项目不像单体脚本它天然是模块化的加一个应用app或加一张表不会伤筋动骨。1.3 环境准备与虚拟环境管理开发环境我建议直接在本地 Linux/macOS 上做Windows 也能做只是个别命令需要调整。这里我把虚拟环境和项目初始化一起讲了。先用 venv 创建虚拟环境这是 Django 项目的第一步。虚拟环境的作用是隔离项目的 Python 依赖不同项目之间不会互相污染特别是你同时在维护两个 Django 项目时一个要 Django 3一个要 Django 4不隔离就会乱套。mkdir django-blog cd django-blog python3 -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate虚拟环境激活后命令行前面会出现(venv)标记这时再安装依赖pip install django pillowpillow是 Python 处理图片的库Django 的ImageField依赖它不安装的话后面上传图片功能会直接报错。创建项目和 Appdjango-admin startproject blogproject . python manage.py startapp blog这里我在项目名后面加了一个点.意思是把manage.py放在当前目录而不是再套一层目录。很多教程没写这个点导致后面目录结构多一层很容易懵。startapp blog则是创建博客这个应用Django 里“项目”project和“应用”app是两个概念项目是整个网站应用是其中的功能模块。一个项目里可以有多个应用比如blog、users、comments每个应用各管一摊。然后记得把blog加到settings.py的INSTALLED_APPS里否则 Django 不认识这个应用。INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, # ... blog, ]2. 数据库与核心模型设计2.1 文章、分类、标签三个核心模型博客的数据模型不复杂但设计得好不好直接影响后续开发的体验。我的设计是这样的Category分类分类是层级简单的对象每个分类有名称和别名。Tag标签标签和文章是多对多关系。Post文章文章包含标题、正文、封面图、视频、所属分类、标签、发布时间、更新时间等。写代码时我建议字段尽量一次想清楚但要留好扩展的余地。比如状态字段我先用draft和published两种状态后续如果要加“置顶”“审核中”只需要加枚举值不需要改表结构。以下是blog/models.py的核心代码from django.db import models from django.urls import reverse from django.utils import timezone class Category(models.Model): name models.CharField(分类名称, max_length50) slug models.SlugField(别名, uniqueTrue) class Meta: verbose_name 分类 verbose_name_plural verbose_name def __str__(self): return self.name class Tag(models.Model): name models.CharField(标签名称, max_length50) slug models.SlugField(别名, uniqueTrue) class Meta: verbose_name 标签 verbose_name_plural verbose_name def __str__(self): return self.name class Post(models.Model): STATUS_CHOICES ( (draft, 草稿), (published, 已发布), ) title models.CharField(标题, max_length200) slug models.SlugField(别名, uniqueTrue) category models.ForeignKey(Category, verbose_name分类, on_deletemodels.PROTECT, related_nameposts) tags models.ManyToManyField(Tag, verbose_name标签, blankTrue, related_nameposts) cover models.ImageField(封面图, upload_tocover/%Y/%m/%d/, blankTrue, nullTrue) video models.FileField(视频, upload_tovideo/%Y/%m/%d/, blankTrue, nullTrue) body models.TextField(正文) status models.CharField(状态, max_length20, choicesSTATUS_CHOICES, defaultdraft) created_at models.DateTimeField(创建时间, defaulttimezone.now) updated_at models.DateTimeField(更新时间, auto_nowTrue) class Meta: verbose_name 文章 verbose_name_plural verbose_name ordering (-created_at,) def __str__(self): return self.title def get_absolute_url(self): return reverse(blog:post_detail, kwargs{slug: self.slug})这里有几个关键点需要展开说。on_deletemodels.PROTECT是特意选的。分类如果被文章引用PROTECT会阻止删除分类并抛出异常避免误删导致整个 blog 文章的分类变成空指针。新手经常用CASCADE但博客场景下分类删除会连带删除所有文章这通常不是你想要的结果。如果你确定某个分类删掉后文章也一起删那用CASCADE没错但博客场景更合理的是PROTECT或SET_NULL。related_name也很重要。设置了related_nameposts后你可以通过category.posts.all()拿到这个分类下的所有文章反向查询语义清晰。如果不设置Django 默认生成category.post_set.all()能看但不够直观。ImageField和FileField的upload_to我做了日期路径这样上传的文件会按cover/2025/06/18/xxx.jpg组织避免一个目录下文件太多。2.2 图片上传与视频上传的字段设计最初设计时图片和视频我合并成了一个通用FileField后来发现不行。ImageField会自动校验文件是不是合法图片并在 Admin 后台生成预览缩略图这对写博客体验很重要。所以封面图用ImageField视频单独用FileField。两个字段我都设置了blankTrue, nullTrue表示封面图和视频不是必填项。注意blank是表单层面的校验null是数据库层面的约束两者一起用才是“可以不填”。视频上传比图片麻烦的地方在于体积和格式。图片一般压缩后几百 KB 到几 MB视频动辄几十 MB 甚至更大。开发环境runserver无所谓但生产环境如果你用的是 Nginx通常还要配一个客户端请求大小的限制比如client_max_body_size 100m;否则上传大视频会直接被挡在 Nginx 层Django 根本收不到请求这个问题我到部署章节再细说。2.3 数据迁移与 ORM 查询/删除对象模型写好后执行迁移python manage.py makemigrations blog python manage.py migratemakemigrations是生成迁移文件不直接改数据库migrate才是把迁移执行到数据库。这两步很多人搞混记住makemigrations是“准备变更”migrate是“应用变更”。写好模型后进入 Django Shell 测试一下 ORM 操作这是个很好的习惯能帮你及时发现问题python manage.py shell在 shell 里可以做这些操作from blog.models import Category, Tag, Post from django.utils import timezone # 创建分类和标签 cat Category.objects.create(namePython, slugpython) tag Tag.objects.create(nameDjango, slugdjango) # 创建文章 post Post.objects.create( title我的第一篇 Django 博客, slugmy-first-django-post, categorycat, body正文内容……, statuspublished, ) # 关联标签 post.tags.add(tag) # 查询 Post.objects.filter(statuspublished) Post.objects.filter(category__namePython) post Post.objects.get(slugmy-first-django-post) # 删除对象 post.delete()这里我要重点说说“删除对象”这个热词。ORM 删除有几种方式区别很大。obj.delete()删除单个对象返回(总删除数, 明细字典)它会把关联的对象按规则处理。QuerySet.delete()批量删除比如Post.objects.filter(categorycat).delete()会删除所有匹配的文章。Category.objects.get(slugpython).delete()由于我们用了PROTECT如果分类下有文章这一步会直接抛出ProtectedError。我在实际项目中遇到过一个问题批量删除时没看返回值以为删干净了结果关联表里还有残留数据。所以删除操作一定要先确认归属关系尤其是有外键和多对多的模型。3. 视图、路由与模板三件套3.1 首页与详情页视图逻辑入门阶段我建议先用函数视图FBV理解流程跑通后再升级到类视图CBV。这里我先展示 FBV 写法后面也会给出 CBV 的版本作为升级参考。blog/views.pyfrom django.shortcuts import render, get_object_or_404 from django.core.paginator import Paginator from .models import Post def post_list(request): posts Post.objects.filter(statuspublished) paginator Paginator(posts, 5) page_number request.GET.get(page) page_obj paginator.get_page(page_number) return render(request, blog/post_list.html, {page_obj: page_obj}) def post_detail(request, slug): post get_object_or_404(Post, slugslug, statuspublished) return render(request, blog/post_detail.html, {post: post})get_object_or_404是 Django 的快捷方式如果查询结果不存在直接返回 404 页面不用手动写 try/except。分页用的是Paginator参数 5 表示每页 5 篇文章。在模板里可以展示上一页/下一页以及页码列表这个后面会讲。升级后的 CBV 版本长这样from django.views.generic import ListView, DetailView from .models import Post class PostListView(ListView): model Post template_name blog/post_list.html context_object_name page_obj paginate_by 5 def get_queryset(self): return super().get_queryset().filter(statuspublished) class PostDetailView(DetailView): model Post template_name blog/post_detail.html slug_url_kwarg slugCBV 代码更少但新手看它会有一种“魔法感”——你得知道ListView默认去查什么、模板里默认上下文变量叫什么。我建议先用 FBV 写一遍再看 CBV 就会豁然开朗。3.2 URL 设计与命名空间在blog/urls.py里定义路由from django.urls import path from . import views app_name blog urlpatterns [ path(, views.post_list, namepost_list), path(post/slug:slug/, views.post_detail, namepost_detail), ]然后在项目的blogproject/urls.py里包含进来from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(blog.urls)), ]注意两个细节第一app_name blog定义了命名空间这样在模板中可以使用{% url blog:post_detail slugpost.slug %}即使将来改了 URL 路径模板代码不需要变动第二slug:slug是 URL 转换器限制参数格式为字母、数字、连字符、下划线避免非法字符。之前我看有人把所有业务路由都写在项目根urls.py里应用一多就变得极难维护。Django 的哲学是“应用自治”每个 app 有自己的urls.py根urls.py只管include这个习惯越早养成越好。3.3 模板继承与页面渲染模板是 Django 里很容易被忽略但很重要的部分。我并没有直接用 Django Admin 的界面作为前台而是自己写了一套模板原因是我们需要理解模板渲染的机制。先创建一个基础模板templates/base.html!DOCTYPE html html langzh-hans head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title{% block title %}我的博客{% endblock %}/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet /head body nav classnavbar navbar-expand-lg navbar-light bg-light div classcontainer a classnavbar-brand href/我的博客/a /div /nav div classcontainer mt-4 {% block content %} {% endblock %} /div /body /html然后templates/blog/post_list.html{% extends base.html %} {% block title %}首页 - 我的博客{% endblock %} {% block content %} h1文章列表/h1 {% for post in page_obj %} article classmb-4 h2 a href{% url blog:post_detail slugpost.slug %}{{ post.title }}/a /h2 p classtext-muted{{ post.created_at|date:Y-m-d }} | {{ post.category.name }}/p {% if post.cover %} img src{{ post.cover.url }} alt{{ post.title }} classimg-fluid stylemax-width: 300px; {% endif %} p{{ post.body|truncatechars:100 }}/p /article {% empty %} p还没有文章去后台发布一篇吧。/p {% endfor %} nav aria-labelPage navigation ul classpagination {% if page_obj.has_previous %} li classpage-itema classpage-link href?page{{ page_obj.previous_page_number }}上一页/a/li {% endif %} li classpage-item disabledspan classpage-link第 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }} 页/span/li {% if page_obj.has_next %} li classpage-itema classpage-link href?page{{ page_obj.next_page_number }}下一页/a/li {% endif %} /ul /nav {% endblock %}templates/blog/post_detail.html{% extends base.html %} {% block title %}{{ post.title }} - 我的博客{% endblock %} {% block content %} article h1{{ post.title }}/h1 p classtext-muted{{ post.created_at|date:Y-m-d H:i }} | {{ post.category.name }}/p {% if post.cover %} img src{{ post.cover.url }} alt{{ post.title }} classimg-fluid {% endif %} div classmt-4 {{ post.body|linebreaks }} /div {% if post.video %} video controls classw-100 mt-3 source src{{ post.video.url }} typevideo/mp4 你的浏览器不支持 video 标签。 /video {% endif %} p classmt-3 标签 {% for tag in post.tags.all %} span classbadge bg-secondary{{ tag.name }}/span {% endfor %} /p /article pa href{% url blog:post_list %}返回列表/a/p {% endblock %}模板继承机制很容易理解base.html是框架block是插槽子模板通过extends继承框架并填充block。这比在每一个页面里复制导航栏、页脚代码要优雅得多。还有几个模板过滤器值得记住|date:Y-m-d格式化日期|truncatechars:100截断文本|linebreaks把换行转成br和p这些都是在实际写作中非常高频的功能。4. Admin 后台与媒体文件管理4.1 注册模型与后台增强Django Admin 是这套框架最大的卖点之一。写博客时我不需要单独做一套后台 UI直接用 Admin 就能管文章、分类、标签。blog/admin.py注册模型from django.contrib import admin from .models import Category, Tag, Post admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display (name, slug) prepopulated_fields {slug: (name,)} admin.register(Tag) class TagAdmin(admin.ModelAdmin): list_display (name, slug) prepopulated_fields {slug: (name,)} admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display (title, category, status, created_at, updated_at) list_filter (status, category, tags) search_fields (title, body) prepopulated_fields {slug: (title,)} filter_horizontal (tags,) date_hierarchy created_at list_editable (status,)prepopulated_fields的作用是在 Admin 页面输入标题时slug 字段会自动根据标题生成省去手动输入的麻烦。注意中文标题生成 slug 会变成空串所以实际发布中文文章时还是需要手动填一下 slug推荐用拼音或者英文字段。filter_horizontal让多对多字段的标签选择变成左右双栏选择体验比默认的下拉框好得多。list_editable让你可以直接在列表页修改状态不用点进详情非常方便。创建超级管理员python manage.py createsuperuser然后启动开发服务器python manage.py runserver访问http://127.0.0.1:8000/admin/登录就可以在后台发布文章了。4.2 静态文件与媒体文件配置刚入门时很多人会把静态文件CSS、JS、图片和媒体文件用户上传的内容混在一起其实它们的处理逻辑完全不同。静态文件是项目自带的资源用STATICFILES_DIRS配置收集目录媒体文件是运行时动态上传的用MEDIA_URL和MEDIA_ROOT配置。settings.py里我建议这样配置import os # 静态文件 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static] STATIC_ROOT BASE_DIR / staticfiles # 媒体文件 MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / mediaSTATIC_ROOT是生产环境执行collectstatic后所有静态文件汇总的目录这是给 Nginx 用的开发环境不需要。如果没设STATIC_ROOT部署时执行collectstatic会报错。开发环境要能正常访问媒体文件还需要在根urls.py中加一段from django.conf import settings from django.conf.urls.static import static urlpatterns [ # ... 你的路由 ] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这段代码只在 DEBUG 模式下生效生产环境媒体文件由 Nginx 直接服务不需要 Django 来处理否则性能会非常差。4.3 富文本/Markdown 方案默认的TextField在 Admin 后台就是一个文本框写文章体验比较原始。我试过几种方案CKEditor、TinyMCE、django-markdownx最后选择了django-markdownx原因是它和 Admin 的集成最顺滑支持实时预览。安装配置pip install django-markdownxsettings.py添加INSTALLED_APPS [ # ... markdownx, ]urls.py添加urlpatterns [ path(markdownx/, include(markdownx.urls)), ]然后把模型里的body字段改成from markdownx.models import MarkdownxField body MarkdownxField(正文)这样 Admin 里就会出现带预览的编辑框写 Markdown 时右侧实时渲染 HTML非常香。不过 Markdown 渲染后前面的post.body|linebreaks就不能直接用了需要改成安全输出。为了简单我建议在post_detail.html里把正文渲染成 HTML 后手动用|safe过滤器或者干脆写一个自定义模板过滤器把 Markdown 转成 HTML。这里给你一个简单的自定义过滤器示例在blog/templatetags/下创建markdown_extras.pyimport markdown from django import template from django.utils.safestring import mark_safe register template.Library() register.filter(namemarkdown) def render_markdown(text): md markdown.Markdown(extensions[extra, codehilite]) return mark_safe(md.convert(text))然后在模板里{% load markdown_extras %} div classmt-4 {{ post.body|markdown }} /div用 Markdown 写博客的好处很多格式干净、不依赖编辑器、版本管理友好。缺点是没有图形界面的排版按钮但博客写作场景完全够用。5. 部署上线与常见问题排查5.1 Linux 服务器部署宝塔环境为例本地开发跑通了距离“全栈”还差最后一步部署。很多新手死在部署这一步其实部署本身不难难的是理解其中的流程。我先说自己踩过的一个大坑直接在生产环境用runserver。Django 官方文档说得非常清楚runserver只适合开发调试性能差且不安全生产环境必须用 WSGI 服务器Gunicorn 或 uWSGI来运行 Django 应用再用 Nginx 反向代理。这里以宝塔面板 Gunicorn Nginx 为例因为宝塔的 Python 项目管理器可以简化不少操作。安装 Gunicornpip install gunicorn在项目根目录执行以下命令验证 Gunicorn 能正常启动gunicorn blogproject.wsgi:application --bind 0.0.0.0:8000blogproject.wsgi:application是 WSGI 入口0.0.0.0:8000表示监听所有网卡的 8000 端口。如果这一步能跑通说明项目基本可以上线了。然后是在 Nginx 中配置反向代理。核心配置如下server { listen 80; server_name your_domain.com; client_max_body_size 100m; location /static/ { alias /www/wwwroot/django-blog/staticfiles/; } location /media/ { alias /www/wwwroot/django-blog/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }部署前先执行python manage.py collectstatic这个命令会把所有静态文件统一复制到STATIC_ROOT指定的目录Nginx 的alias指向这里。如果不执行collectstatic你会看到一个只有 HTML 没有样式的页面甚至控制台报 404。生产环境里settings.py还需要修改几个关键项DEBUG False ALLOWED_HOSTS [your_domain.com, your_server_ip]DEBUG False之后Django 不再输出详细的错误页面而是显示一个通用 500 页面。如果你需要排查错误可以看日志或者临时把DEBUG打开。5.2 高频报错与排查实录我整理了一张速查表基本覆盖了 Django 博客项目从开发到部署最常见的报错报错信息原因解决方案ModuleNotFoundError: No module named PIL没装 Pillowpip install pillowNo migrations to apply迁移文件已是最新或数据库表已存在检查migrations目录必要时python manage.py migrate --fakeTemplateDoesNotExist模板路径不对或APP_DIRS未开检查TEMPLATES配置和templates目录位置Invalid HTTP_HOST headerALLOWED_HOSTS没配置在ALLOWED_HOSTS中加入域名或 IPBad Request (400)ALLOWED_HOSTS配置不正确同上DisallowedHost请求的 Host 不在ALLOWED_HOSTS中同上SyntaxError: (unicode error)Python 版本或编码问题使用 UTF-8 编码检查 Python 版本OperationalError: no such column模型字段改动后没迁移python manage.py makemigrations python manage.py migrateAttributeError: NoneType object has no attribute splitSECRET_KEY读取失败或环境变量缺失检查环境变量UnicodeDecodeError文件编码问题在 Linux 上设置export LC_ALLC.UTF-8413 Request Entity Too Large上传文件超过 Nginx 限制设置client_max_body_size502 Bad GatewayGunicorn 没启动或端口不对检查 Gunicorn 进程与 Nginx proxy_pass 是否一致这里我单独说一个最常见的坑python manage.py migrate报django.db.utils.ProgrammingError: relation blog_post already exists。这种情况通常是你之前migrate过一次后来又手动删了数据库但迁移表的记录还在或者你手动删了表但迁移记录还在。解决方法是进django_migrations表看一下记录确认哪些迁移已经被记录。如果确实是自己手动改动了数据库结构最简单的办法是备份数据后把blog应用的所有迁移文件删掉重新生成再执行migrate。注意千万别在生产环境随便删迁移删了之后恢复数据很麻烦。5.3 安全与性能注意事项入门项目上线后有几个安全项必须检查SECRET_KEY不要硬编码在代码里尤其别推到公开仓库。我自己遇到过密钥泄露导致 CSRF 校验失败的教训。数据库不要用默认的 SQLite 跑生产环境。SQLite 在低并发场景还能用但换到 MySQL 或 PostgreSQL 才是长久的方案。Django 的 ORM 屏蔽了大部分差异切换代价不大。上传文件一定要限制类型和大小。虽然 Django 的 ImageField 会校验图片格式但恶意文件仍然可能通过 FileField 上传。安全起见可以在视图或表单层加一层扩展名校验。性能方面博客项目在初期不会遇到太大瓶颈但有一点值得提前做给常用查询加select_related或prefetch_related避免 N1 查询。比如文章列表页如果每篇文章都显示分类名默认会为每篇文章单独查询一次分类表文章多了性能就低了。优化方式是posts Post.objects.filter(statuspublished).select_related(category)对于多对多的 tags用prefetch_related(tags)。我在做列表页时加了这个优化后100 篇文章的页面查询从 100 次 SQL 降到了 3 次效果非常明显。6. 后续扩展方向6.1 从 Django REST Framework 走向前后端分离如果你把博客的 HTML 模板版本做完了下一步我建议学 Django REST FrameworkDRF把同一个博客改造成 API 服务。前后端分离是目前全栈开发的主流形态后端只提供 JSON 接口前端用 Vue 或 React 单独开发。DRF 的使用思路和 Django 一脉相承。你不需要重新设计数据库只需要为现有的Post模型写序列化器from rest_framework import serializers from .models import Post class PostSerializer(serializers.ModelSerializer): class Meta: model Post fields [id, title, slug, category, tags, created_at]然后写一个视图集和路由就能对外暴露 API 了。这种渐进式扩展的好处是你的数据模型不用推翻重来只需加一层接口层。6.2 Django Vue 整合实操“Django 和 Vue 整合”是热词这个话题看起来复杂其实核心就一句话Vue 负责前端页面Django 负责 API。实际项目里有两种常见做法。第一种是前后端完全分离Django 部署为 API 服务Vue 打包后的静态文件放在 Nginx 里两者通过/api/路径通信。第二种是混合开发把 Vue 打包后放到 Django 的static目录中由 Django 直接托管前端页面适合小项目部署。我做过第二种方案体验不错。具体步骤是用 Vite 构建 Vue 项目npm run build生成dist目录然后把dist下的内容复制到 Django 的static/vue/目录再在 Django 中写一个视图渲染index.html。这样部署时只需要跑一个 Django 服务省心不少。6.3 继续补充的全栈技能点如果你打算以全栈开发为目标做完了这个博客后还可以继续挑战以下技能点给文章加评论功能学习 Django 的Form、ModelForm、CSRF 防护和用户认证。加一个搜索功能可以先在 Django 里用icontains做最简单的模糊搜索再了解 Elasticsearch 这类搜索引擎的接入方式。加一个 RSS 订阅Django 内置了django.contrib.syndication几行代码就能给博客加上 RSS 输出。了解 Docker 部署把 Django、Nginx、MySQL 全部打包成容器这是目前企业里很常见的交付方式。这个博客项目只是起点但它把全栈开发的骨架搭了出来前端页面、后端接口、数据库模型、文件上传、部署上线你都已经走了一遍。后面每一个扩展方向都是在往这个骨架上添加更专业的血肉。按照我个人的经验把一个简单的项目完整走完比跟着教程敲十遍代码要有效得多。希望这篇实战笔记能帮你少踩一些我踩过的坑。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →