95·模块十八:综合项目实战高级

项目实战:开发一个博客系统(Django/Flask)

projectweb-developmentfull-stack

第95课:项目实战 - 开发一个博客系统(Django/Flask)

学习目标

完成本课学习后,你将能够:

  1. 理解并选择:根据项目需求,在Django和Flask这两个主流Python Web框架之间做出合适的选择。
  2. 设计数据模型:为博客系统设计包含用户、文章、评论等核心实体的数据库模型。
  3. 实现核心功能:使用所选框架完成文章列表、详情、创建、用户认证等核心功能的开发。
  4. 组织项目结构:遵循最佳实践来组织Web项目的文件和目录结构。
  5. 完成项目部署准备:了解将开发环境项目推向生产环境所需的基本步骤。

核心概念

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)"功能。提示:

  1. 创建一个新的 Category 模型。
  2. Post 模型中添加一个指向 Category 的外键。
  3. 创建一个视图来显示某个分类下的所有文章。
  4. 在文章列表页或侧边栏显示分类列表。

练习3:用户系统完善

利用Django内置的认证系统 (django.contrib.auth),实现以下功能:

  1. 用户注册页面。
  2. 用户登录和注销页面。
  3. 只有登录用户才能发表评论(修改评论视图和模板)。
  4. 创建一个页面来展示当前登录用户发表的所有文章。

常见错误

  1. 忘记运行数据库迁移:修改models.py后,没有执行 makemigrationsmigrate 命令,导致数据库表结构与代码不符,出现"no such table"错误。
  2. 模板路径错误:Django会在应用下的templates目录和项目settings.pyTEMPLATES设置指定的目录中寻找模板。如果模板文件放错位置或DIRS设置不对,会报"TemplateDoesNotExist"错误。
  3. 视图函数未返回HttpResponse:每个视图函数必须返回一个HttpResponse对象(render()是其快捷方式)。忘记return会导致服务器错误。
  4. 外键关系设置不当:在设置ForeignKey时,需要考虑当关联对象被删除时应该如何处理当前对象(on_delete参数)。常用CASCADE表示级联删除。
  5. 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 等扩展来重新实现相同的功能,以加深对不同框架哲学的理解。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「项目实战:完成一个端到端的数据分析项目」 以巩固所学知识。