第十一章:Django 博客项目实战 —— 从零构建完整 Web 应用 核心关键词:Django 项目实战、博客系统开发、Django ORM、类视图、URL 路由配置、模板渲染、数据库迁移、管理后台集成 本章通过构建一个功能完备的博客系统,系统性地串联 Django 核心开发流程。项目涵盖项目初始化、模型设计、数据库迁移、基于类视图(CBV)的业务逻辑实现、模板驱动的前端渲染、RESTful 风格 URL 路由、表单自动化处理及管理后台集成等关键环节。所有代码均遵循 Django 最佳实践,结构清晰、可扩展性强,为后续企业级应用开发奠定坚实基础。
核心关键词:Django 项目实战、博客系统开发、Django ORM、类视图、URL 路由配置、模板渲染、数据库迁移、管理后台集成
本章通过构建一个功能完备的博客系统,系统性地串联 Django 核心开发流程。项目涵盖项目初始化、模型设计、数据库迁移、基于类视图(CBV)的业务逻辑实现、模板驱动的前端渲染、RESTful 风格 URL 路由、表单自动化处理及管理后台集成等关键环节。所有代码均遵循 Django 最佳实践,结构清晰、可扩展性强,为后续企业级应用开发奠定坚实基础。
使用 Django 命令行工具完成项目骨架搭建:
django-admin startproject blogproject cd blogproject
生成的标准目录结构如下:
blogproject/ ├── manage.py └── blogproject/ ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py
manage.py:项目管理入口,用于运行开发服务器、执行迁移、创建超级用户等;blogproject/settings.py:核心配置文件,控制数据库、静态资源、中间件、已安装应用等;blogproject/urls.py:项目级 URL 路由中枢,负责分发请求至各应用;asgi.py / wsgi.py:分别适配异步(ASGI)与同步(WSGI)生产部署环境。在项目根目录执行:
python manage.py startapp blogapp
blogapp/ 目录结构:
blogapp/ ├── __init__.py ├── admin.py ├── apps.py ├── migrations/ │ └── __init__.py ├── models.py ├── views.py └── tests.py
编辑 blogproject/settings.py,在 INSTALLED_APPS 列表末尾添加:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blogapp', # 新增:注册 blogapp 应用 ]
✅ 关键说明:Django 项目是容器,应用是功能模块;一个项目可集成多个职责分离的应用(如
user,payment,notification),提升可维护性与复用性。
在 blogapp/models.py 中定义两个核心模型:Category(分类)与 Post(文章),体现一对多关系。
from django.db import models from django.utils import timezone class Category(models.Model): name = models.CharField(max_length=100, unique=True, verbose_name="分类名称") class Meta: verbose_name = "分类" verbose_name_plural = "分类管理" ordering = ["name"] def __str__(self): return self.name class Post(models.Model): title = models.CharField(max_length=200, verbose_name="标题") content = models.TextField(verbose_name="正文") pub_date = models.DateTimeField( default=timezone.now, verbose_name="发布时间", db_index=True # 加速按时间排序查询 ) category = models.ForeignKey( Category, on_delete=models.CASCADE, related_name="posts", verbose_name="所属分类" ) class Meta: verbose_name = "文章" verbose_name_plural = "文章管理" ordering = ["-pub_date"] def __str__(self): return self.title
字段设计要点:
verbose_name:为 Admin 后台与表单提供中文标签;unique=True(Category.name):避免重复分类;db_index=True(pub_date):提升时间范围查询性能;related_name="posts":支持反向查询 category.posts.all();ordering:全局默认排序,减少视图中重复指定。✅ 设计原则:模型即业务实体,字段需兼顾语义清晰性、数据完整性与查询效率。
执行两步迁移命令,生成并应用数据库变更:
# 1. 生成迁移文件(基于模型差异) python manage.py makemigrations blogapp # 2. 应用迁移至数据库(默认 SQLite) python manage.py migrate blogapp
生成的迁移文件 blogapp/migrations/0001_initial.py 包含完整建表逻辑,包含主键、外键约束与索引定义。
✅ 验证方式:检查项目根目录生成的
db.sqlite3文件,或运行python manage.py dbshell进入 SQLite 交互终端,执行.tables查看blogapp_category与blogapp_post表。
blogapp/views.py 使用 Django 内置通用类视图(Generic Class-Based Views),显著减少样板代码:
from django.shortcuts import render, get_object_or_404, redirect from django.views import generic from django.urls import reverse_lazy from .models import Post, Category from .forms import PostForm class PostListView(generic.ListView): model = Post template_name = "blogapp/post_list.html" context_object_name = "posts" paginate_by = 10 # 启用分页(后续扩展) ordering = ["-pub_date"] class PostDetailView(generic.DetailView): model = Post template_name = "blogapp/post_detail.html" context_object_name = "post" class PostCreateView(generic.CreateView): model = Post form_class = PostForm template_name = "blogapp/post_form.html" success_url = reverse_lazy("blogapp:post_list") class PostUpdateView(generic.UpdateView): model = Post form_class = PostForm template_name = "blogapp/post_form.html" context_object_name = "post" success_url = reverse_lazy("blogapp:post_list") class PostDeleteView(generic.DeleteView): model = Post template_name = "blogapp/post_confirm_delete.html" context_object_name = "post" success_url = reverse_lazy("blogapp:post_list")
blogapp/forms.pyfrom django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ["title", "content", "category"] widgets = { "title": forms.TextInput(attrs={"class": "form-control", "placeholder": "请输入文章标题"}), "content": forms.Textarea(attrs={"class": "form-control", "rows": 8, "placeholder": "请输入文章正文"}), "category": forms.Select(attrs={"class": "form-control"}), } labels = { "title": "标题", "content": "正文", "category": "分类", }
✅ 优势体现:
CreateView/UpdateView自动处理表单验证、模型保存与重定向;widgets和labels提升前端用户体验与可访问性。
在 blogapp/ 目录下创建标准模板路径:blogapp/templates/blogapp/
为保持一致性,建议创建 base.html 作为母版:
<!-- blogapp/templates/blogapp/base.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>{% block title %}Django 博客{% endblock %}</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body class="bg-light"> <div class="container mt-4"> <header class="mb-4"> <h1 class="text-primary">📝 Django 博客系统</h1> </header> <main> {% block content %}{% endblock %} </main> <footer class="mt-5 py-3 text-muted text-center"> <p>© 2024 Django 博客项目实战</p> </footer> </div> </body> </html>
post_list.html(继承 base.html):
{% extends "blogapp/base.html" %} {% block title %}文章列表 - {{ block.super }}{% endblock %} {% block content %} <h2 class="mb-4">全部文章</h2> <a href="{% url 'blogapp:post_create' %}" class="btn btn-success mb-3">+ 创建新文章</a> {% if posts %} <ul class="list-group"> {% for post in posts %} <li class="list-group-item"> <h5><a href="{% url 'blogapp:post_detail' pk=post.pk %}" class="text-decoration-none">{{ post.title }}</a></h5> <small class="text-muted">发布时间:{{ post.pub_date|date:"Y-m-d H:i" }} | 分类:<strong>{{ post.category.name }}</strong></small> <div class="mt-2"> <a href="{% url 'blogapp:post_update' pk=post.pk %}" class="btn btn-sm btn-outline-primary me-1">编辑</a> <a href="{% url 'blogapp:post_delete' pk=post.pk %}" class="btn btn-sm btn-outline-danger">删除</a> </div> </li> {% endfor %} </ul> {% include "blogapp/pagination.html" %} {% else %} <div class="alert alert-info">暂无文章,请先创建。</div> {% endif %} {% endblock %}
post_detail.html:
{% extends "blogapp/base.html" %} {% block title %}{{ post.title }} - {{ block.super }}{% endblock %} {% block content %} <article class="card mb-4"> <div class="card-body"> <h1 class="card-title">{{ post.title }}</h1> <p class="text-muted mb-3"> 发布时间:{{ post.pub_date|date:"Y年m月d日 H:i" }} | 分类:<strong>{{ post.category.name }}</strong> </p> <div class="card-text">{{ post.content|linebreaks }}</div> </div> </article> <a href="{% url 'blogapp:post_list' %}" class="btn btn-secondary">← 返回列表</a> {% endblock %}
post_form.html:
{% extends "blogapp/base.html" %} {% block title %}{% if form.instance.pk %}编辑文章{% else %}创建文章{% endif %} - {{ block.super }}{% endblock %} {% block content %} <h2 class="mb-4">{% if form.instance.pk %}编辑文章{% else %}创建文章{% endif %}</h2> <form method="post" class="needs-validation" novalidate> {% csrf_token %} <div class="row g-3"> <div class="col-12"> <label for="{{ form.title.id_for_label }}" class="form-label">{{ form.title.label }}</label> {{ form.title }} {% if form.title.errors %}<div class="text-danger">{{ form.title.errors }}</div>{% endif %} </div> <div class="col-12"> <label for="{{ form.category.id_for_label }}" class="form-label">{{ form.category.label }}</label> {{ form.category }} {% if form.category.errors %}<div class="text-danger">{{ form.category.errors }}</div>{% endif %} </div> <div class="col-12"> <label for="{{ form.content.id_for_label }}" class="form-label">{{ form.content.label }}</label> {{ form.content }} {% if form.content.errors %}<div class="text-danger">{{ form.content.errors }}</div>{% endif %} </div> </div> <div class="mt-4"> <button type="submit" class="btn btn-primary me-2">保存</button> <a href="{% url 'blogapp:post_list' %}" class="btn btn-secondary">取消</a> </div> </form> {% endblock %}
✅ 模板优化点:
- 使用 Bootstrap 5 快速构建响应式 UI;
|linebreaks过滤器将换行符转为<br>;|date格式化时间显示;- 表单错误显式提示,增强用户反馈。
blogapp/urls.pyfrom django.urls import path from . import views app_name = "blogapp" urlpatterns = [ path("", views.PostListView.as_view(), name="post_list"), path("post/<int:pk>/", views.PostDetailView.as_view(), name="post_detail"), path("post/create/", views.PostCreateView.as_view(), name="post_create"), path("post/<int:pk>/update/", views.PostUpdateView.as_view(), name="post_update"), path("post/<int:pk>/delete/", views.PostDeleteView.as_view(), name="post_delete"), ]
blogproject/urls.pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), path("", include("blogapp.urls")), # 根路径交由 blogapp 处理 ]
✅ 最佳实践:始终使用
app_name定义命名空间,模板中通过{% url 'blogapp:post_list' %}反向解析,确保 URL 变更时全站自动更新。
blogapp/admin.py:
from django.contrib import admin from .models import Post, Category @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ["name", "created_at"] search_fields = ["name"] @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ["title", "category", "pub_date", "created_at"] list_filter = ["category", "pub_date"] search_fields = ["title", "content"] date_hierarchy = "pub_date" filter_horizontal = [] # 无多对多字段,留空
✅ Admin 增强:添加搜索、筛选、时间分层导航,大幅提升后台操作效率。
python manage.py createsuperuser python manage.py runserver
访问以下地址验证功能:
http://127.0.0.1:8000/ → 博客前台http://127.0.0.1:8000/admin/ → 管理后台(创建分类后,发布文章即可关联)本章完整实现了 Django 博客系统的 MVP(最小可行产品),覆盖了 Web 开发全生命周期核心能力:
| 模块 | 关键技术点 | 实践价值 |
|---|---|---|
| 项目架构 | startproject / startapp / INSTALLED_APPS |
理解 Django 分层设计哲学 |
| 数据建模 | ForeignKey / verbose_name / ordering / db_index |
掌握生产级模型设计规范 |
| 视图开发 | ListView, DetailView, CreateView 等 CBV |
大幅提升开发效率与代码复用率 |
| 模板工程 | 继承、块覆盖、过滤器、表单渲染 | 构建可维护、可扩展的前端结构 |
| 路由系统 | 命名空间、路径参数、reverse_lazy |
实现松耦合、高内聚的 URL 管理 |
| 后台管理 | ModelAdmin 定制、搜索/筛选/分层 |
快速交付运营支撑能力 |
LoginRequiredMixin 实现登录态保护ForeignKey 关联 Post 与 User,支持嵌套回复django-haystack 或 PostgreSQL fulltext searchdjango-ckeditor 替代原生 Textarea🌟 核心价值:本项目不仅是学习样本,更是可直接投入二次开发的生产就绪模板。所有代码严格遵循 PEP 8 与 Django 官方风格指南,注释完备、结构清晰,为团队协作与长期演进提供坚实保障。