第95课:项目实战 - 开发一个博客系统(Django/Flask)
学习目标
完成本课学习后,你将能够:
- 理解并选择:根据项目需求,在Django和Flask这两个主流Python Web框架之间做出合适的选择。
- 设计数据模型:为博客系统设计包含用户、文章、评论等核心实体的数据库模型。
- 实现核心功能:使用所选框架完成文章列表、详情、创建、用户认证等核心功能的开发。
- 组织项目结构:遵循最佳实践来组织Web项目的文件和目录结构。
- 完成项目部署准备:了解将开发环境项目推向生产环境所需的基本步骤。
核心概念
Django vs. Flask:如何选择?
想象一下建房子:
- Django 就像一套精装样板间,自带了厨房(用户认证)、卫生间(后台管理)、水电管线(ORM)等。你直接入住,按照你的喜好进行微调即可。它采用"全栈式"和"约定优于配置"的理念。
- Flask 则是一片空地和一些建筑材料。你需要自己设计蓝图,砌墙、通水电。它更加轻量和灵活,被称为"微框架",但核心功能(如数据库连接、用户认证)需要通过扩展来添加。
对于我们的博客系统,Django 通常是更高效的起点,因为它内置了我们急需的用户认证系统和强大的后台管理界面,能让我们快速搭建起系统骨架。本课将以 Django 为例进行讲解。
核心概念:模型 (Model)、视图 (View)、模板 (Template)
Django 遵循经典的 MVT (Model-View-Template) 设计模式,与 MVC 模式类似。
- 模型 (Model):数据的唯一、确定的数据源。它定义了数据的结构(就像数据库表的设计图),并包含了操作数据的方法。在Django中,我们用Python类来定义模型。
- 视图 (View):业务逻辑层。它接收用户的请求,从模型中获取数据,然后调用模板生成要返回给用户的响应(HTML页面、JSON数据等)。
- 模板 (Template):展示层。它定义了数据如何呈现给用户。它通常包含HTML代码,以及一些特殊的模板语法(如
{{ variable }})来插入动态数据。
我们的博客数据模型设计
一个最基础的博客需要:
- 用户 (User):可以注册、登录、发表文章。Django自带强大的
User模型。 - 文章 (Post):包含标题、正文、发布时间、作者。一篇
文章属于一个用户。 - 评论 (Comment):读者对文章的反馈。一条
评论关联一篇文章和一个用户。
代码示例
让我们使用Django开始搭建博客的核心部分。
1. 项目初始化与创建应用
# 安装Django
pip install django
# 创建项目
django-admin startproject mysite
cd mysite
# 创建一个应用(App),博客功能放在"blog"应用中
python manage.py startapp blog
2. 定义模型 (blog/models.py)
from django.db import models
from django.contrib.auth.models import User # 引入Django自带的用户模型
from django.urls import reverse
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True, allow_unicode=True) # URL友好标识
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now) # 需要 from django.utils import timezone
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
class Meta:
ordering = ('-publish',) # 按发布时间降序排列
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail', args=[self.slug]) # 用于生成文章详情页URL
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.name} on {self.post}'
3. 创建视图 (blog/views.py)
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
"""文章列表视图"""
posts = Post.objects.filter(status='published')
return render(request, 'blog/post/list.html', {'posts': posts})
def post_detail(request, slug):
"""文章详情视图"""
post = get_object_or_404(Post, slug=slug, status='published')
return render(request, 'blog/post/detail.html', {'post': post})
4. 配置URL (blog/urls.py - 需要创建)
from django.urls import path
from . import views
app_name = 'blog' # URL命名空间
urlpatterns = [
# 文章列表页,对应根路径
path('', views.post_list, name='post_list'),
# 文章详情页,<slug:post_slug> 是动态部分
path('<slug:post_slug>/', views.post_detail, name='post_detail'),
]
然后在项目的主URL配置文件 (mysite/urls.py) 中引入应用的URL:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace='blog')), # 所有以blog/开头的URL交给blog应用处理
]
5. 创建基础模板 (blog/templates/blog/base.html)
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Blog{% endblock %}</title>
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>
6. 创建文章列表模板 (blog/templates/blog/post/list.html)
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<article>
<h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
<p>Published {{ post.publish }} by {{ post.author }}</p>
{{ post.body|truncatewords:30 }} <!-- 只显示前30个单词 -->
</article>
{% endfor %}
{% endblock %}
7. 注册模型到Admin (blog/admin.py)
from django.contrib import admin
from .models import Post, Comment
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish', 'status')
list_filter = ('status', 'created', 'publish', 'author')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'publish'
ordering = ['status', 'publish']
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'post', 'created', 'active')
list_filter = ('active', 'created', 'updated')
search_fields = ('name', 'email', 'body')
完成以上步骤后,执行:
python manage.py makemigrations blog
python manage.py migrate
python manage.py createsuperuser # 创建管理员账号
python manage.py runserver
访问 http://127.0.0.1:8000/admin/ 你就能使用后台管理博客内容了,访问 http://127.0.0.1:8000/blog/ 就能看到文章列表页(需要先在后台添加几篇文章)。
实践练习
练习1:基础扩展
在现有项目基础上,为文章详情页 (post_detail) 添加显示文章下方所有评论的功能,并创建一个评论提交表单。
预期:文章详情页下方应有"评论"列表和"添加评论"的表单。
练习2:功能增强
为博客系统添加"分类 (Category)"功能。提示:
- 创建一个新的
Category模型。 - 在
Post模型中添加一个指向Category的外键。 - 创建一个视图来显示某个分类下的所有文章。
- 在文章列表页或侧边栏显示分类列表。
练习3:用户系统完善
利用Django内置的认证系统 (django.contrib.auth),实现以下功能:
- 用户注册页面。
- 用户登录和注销页面。
- 只有登录用户才能发表评论(修改评论视图和模板)。
- 创建一个页面来展示当前登录用户发表的所有文章。
常见错误
- 忘记运行数据库迁移:修改
models.py后,没有执行makemigrations和migrate命令,导致数据库表结构与代码不符,出现"no such table"错误。 - 模板路径错误:Django会在应用下的
templates目录和项目settings.py中TEMPLATES设置指定的目录中寻找模板。如果模板文件放错位置或DIRS设置不对,会报"TemplateDoesNotExist"错误。 - 视图函数未返回
HttpResponse:每个视图函数必须返回一个HttpResponse对象(render()是其快捷方式)。忘记return会导致服务器错误。 - 外键关系设置不当:在设置
ForeignKey时,需要考虑当关联对象被删除时应该如何处理当前对象(on_delete参数)。常用CASCADE表示级联删除。 - URL配置错误:忘记在项目的主
urls.py中使用include()引入应用的URL配置,或者app_name命名空间使用错误,会导致404错误。
小结
本节课我们以 Django 框架为例,实战演练了一个基础博客系统的搭建过程。关键要点回顾如下:
- 框架选择:Django以其"自带电池"的特性,非常适合像博客这类需要完整认证和管理后台的全栈Web项目的快速开发。
- MVT模式:理解并实践了 模型 (Models) 定义数据、视图 (Views) 处理逻辑、模板 (Templates) 负责展示的分工协作模式。
- 核心工作流:掌握了从定义模型、执行迁移、创建视图、配置URL到编写模板的完整Web开发循环。
- 后台管理:利用Django强大的Admin后台,几乎零代码就实现了对博客内容的增删改查管理。
- 项目结构:学会了将功能划分为独立的应用 (App),并遵循标准的目录结构组织代码。
下一步,你可以将本课的博客系统与第94课学习的 RESTful API 结合,为其添加API接口;或者继续完善用户系统、部署上线,将这个项目变成一个真正可用的产品。对于更轻量或高度定制的需求,也可以尝试使用 Flask 配合 Flask-SQLAlchemy, Flask-Login 等扩展来重新实现相同的功能,以加深对不同框架哲学的理解。