1 整体快速规划设计

1.1 系统软件体系架构

在这里插入图片描述

1.2 系统功能设计

文章管理:查看列表、文章详情、发表文章
用户认证:注册新用户、用户登录、用户注销
密码管理:修改密码(含完整性验证)
在这里插入图片描述

2 环境与体系架构

2.1 创建项目和应用

D:\myblogDsn>django-admin startproject myblogDsn
D:\myblogDsn>cd myblogDsn
D:\myblogDsn>python manage.py startapp blog
D:\myblogDsn>python manage.py startapp users

2.2 配置项目结构

设计myblog/settings.py,

import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
ROOT_URLCONF = 'myblogDsn.urls'
WSGI_APPLICATION = 'myblogDsn.wsgi.application'

# 安全配置(保留你自己的 SECRET_KEY)
SECRET_KEY = 'django-insecure-你原来的密钥'
DEBUG = True
ALLOWED_HOSTS = []

# 应用列表(你的应用已经配置好了)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',   # 博客应用
    'users',  # 用户应用
]

# 【核心】中间件配置(解决之前的 SystemCheckError)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# 【核心】模板配置(解决 admin.E003 错误,匹配你的 templates 目录)
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 告诉 Django 全局模板目录的位置
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# 数据库配置(默认 SQLite,不用改)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# 中文和时区配置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True

# 【核心】静态文件配置(匹配你的 static 目录,解决警告)
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static",
]

# 登录相关配置(你之前的配置,保留即可)
LOGIN_URL = '/users/login/'
LOGIN_REDIRECT_URL = '/blog/'

# Django 3.2+ 必须的配置
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

3 系统集编码设计

3.1 定义模型

设计blog/models.py

from django.db import models
from django.contrib.auth.models import User


class Article(models.Model):
    """文章模型"""
    title = models.CharField('标题', max_length=200)
    content = models.TextField('内容')
    author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
    created_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('更新时间', auto_now=True)

    class Meta:
        verbose_name = '文章'
        verbose_name_plural = '文章'
        ordering = ['-created_at']

    def __str__(self):
        return self.title

# Create your models here.

3.2 创建管理后台

blog/admin.py

from django.contrib import admin
from .models import Article

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'created_at')
    list_filter = ('created_at', 'author')
    search_fields = ('title', 'content')


# Register your models here.

3.3 创建视图

blog/view.py

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from .models import Article
from .forms import ArticleForm

def article_list(request):
    """文章列表页"""
    articles = Article.objects.all()
    return render(request, 'blog/article_list.html', {'articles': articles})

def article_detail(request, pk):
    """文章详情页"""
    article = get_object_or_404(Article, pk=pk)
    return render(request, 'blog/article_detail.html', {'article': article})

@login_required
def article_create(request):
    """发表文章(需登录)"""
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save(commit=False)
            article.author = request.user
            article.save()
            return redirect('article_detail', pk=article.pk)
    else:
        form = ArticleForm()
    return render(request, 'blog/article_form.html', {'form': form, 'action': '发表'})


# Create your views here.

3.4 基本窗口构造

blog/forms.py

from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入文章标题'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10, 'placeholder': '请输入文章内容'}),
        }
        labels = {
            'title': '标题',
            'content': '内容',
        }

3.5 配置URL路由

blog/urls.py

from django.urls import path
from blog import views  # 关键:从 blog 应用导入 views

urlpatterns = [
    path('', views.article_list, name='article_list'),
    path('article/<int:pk>/', views.article_detail, name='article_detail'),
    path('create/', views.article_create, name='article_create'),
]

4 页面及其服务设计

4.1 创建表单

users/ forms.py
用户注册表单,包含完整的验证逻辑:

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import authenticate


class LoginForm(forms.Form):
    """登录表单"""
    username = forms.CharField(
        label='用户名',
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入用户名'})
    )
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'})
    )


class RegisterForm(forms.Form):
    """注册表单,包含完整的验证逻辑"""
    username = forms.CharField(
        label='用户名',
        max_length=20,
        min_length=3,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '3-20个字符'}),
        error_messages={
            'required': '用户名不能为空',
            'min_length': '用户名至少3个字符',
            'max_length': '用户名最多20个字符'
        }
    )
    email = forms.EmailField(
        label='邮箱',
        max_length=50,
        widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入邮箱'}),
        error_messages={'required': '邮箱不能为空', 'invalid': '请输入有效的邮箱地址'}
    )
    password = forms.CharField(
        label='密码',
        min_length=6,
        max_length=18,
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '6-18个字符'}),
        error_messages={
            'required': '密码不能为空',
            'min_length': '密码至少6个字符',
            'max_length': '密码最多18个字符'
        }
    )
    confirm_password = forms.CharField(
        label='确认密码',
        min_length=6,
        max_length=18,
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '再次输入密码'}),
        error_messages={
            'required': '确认密码不能为空',
            'min_length': '确认密码至少6个字符',
            'max_length': '确认密码最多18个字符'
        }
    )

    def clean_username(self):
        """验证用户名唯一性"""
        username = self.cleaned_data['username']
        if User.objects.filter(username=username).exists():
            raise forms.ValidationError('该用户名已被注册')
        return username

    def clean_email(self):
        """验证邮箱唯一性"""
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError('该邮箱已被注册')
        return email

    def clean(self):
        """验证两次密码一致性"""
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')

        if password and confirm_password and password != confirm_password:
            self.add_error('confirm_password', '两次密码输入不一致')

        return cleaned_data


class ChangePasswordForm(forms.Form):
    """修改密码表单"""
    old_password = forms.CharField(
        label='原密码',
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入原密码'}),
        error_messages={'required': '原密码不能为空'}
    )
    new_password = forms.CharField(
        label='新密码',
        min_length=6,
        max_length=18,
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '6-18个字符'}),
        error_messages={
            'required': '新密码不能为空',
            'min_length': '新密码至少6个字符',
            'max_length': '新密码最多18个字符'
        }
    )
    confirm_new_password = forms.CharField(
        label='确认新密码',
        min_length=6,
        max_length=18,
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '再次输入新密码'}),
        error_messages={
            'required': '确认新密码不能为空',
            'min_length': '确认新密码至少6个字符',
            'max_length': '确认新密码最多18个字符'
        }
    )

    def __init__(self, user, *args, **kwargs):
        """重写__init__,传入当前用户对象"""
        self.user = user
        super().__init__(*args, **kwargs)

    def clean_old_password(self):
        """验证原密码是否正确"""
        old_password = self.cleaned_data['old_password']
        if not self.user.check_password(old_password):
            raise forms.ValidationError('原密码不正确')
        return old_password

    def clean_new_password(self):
        """验证新密码是否与原密码相同"""
        new_password = self.cleaned_data['new_password']
        old_password = self.cleaned_data.get('old_password', '')
        if new_password == old_password:
            raise forms.ValidationError('新密码不能与原密码相同')
        return new_password

    def clean(self):
        """验证两次新密码一致性"""
        cleaned_data = super().clean()
        new_password = cleaned_data.get('new_password')
        confirm_new_password = cleaned_data.get('confirm_new_password')

        if new_password and confirm_new_password and new_password != confirm_new_password:
            self.add_error('confirm_new_password', '两次密码输入不一致')

        return cleaned_data

4.2 创建视图

(user/views.py)

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import LoginForm, RegisterForm, ChangePasswordForm


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, username=username, password=password)
            if user is not None:
                login(request, user)
                messages.success(request, f'欢迎回来,{username}!')
                # 获取next参数,实现登录后跳转到原页面
                next_url = request.GET.get('next', '/blog/')
                return redirect(next_url)
            else:
                messages.error(request, '用户名或密码错误')
    else:
        form = LoginForm()
    return render(request, 'users/login.html', {'form': form})


def user_register(request):
    """用户注册"""
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']

            # 创建用户
            user = User.objects.create_user(
                username=username,
                email=email,
                password=password
            )

            # 注册后自动登录
            login(request, user)
            messages.success(request, f'注册成功,欢迎 {username}!')
            return redirect('/blog/')
    else:
        form = RegisterForm()
    return render(request, 'users/register.html', {'form': form})


def user_logout(request):
    """用户注销"""
    logout(request)
    messages.success(request, '您已成功退出登录')
    return redirect('/blog/')


@login_required
def change_password(request):
    """修改密码"""
    if request.method == 'POST':
        form = ChangePasswordForm(request.user, request.POST)
        if form.is_valid():
            new_password = form.cleaned_data['new_password']
            # 更新密码
            request.user.set_password(new_password)
            request.user.save()
            # 更新密码后重新登录
            login(request, request.user)
            messages.success(request, '密码修改成功!')
            return redirect('/blog/')
    else:
        form = ChangePasswordForm(request.user)
    return render(request, 'users/change_password.html', {'form': form})

# Create your views here.

4.3 配置URL路由

(users/urls.py)

from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.user_login, name='login'),
    path('register/', views.user_register, name='register'),
    path('logout/', views.user_logout, name='logout'),
    path('change_password/', views.change_password, name='change_password'),
]

(myblogDsn/urls.py)

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
   path('admin/', admin.site.urls),
   path('blog/', include('blog.urls')),
   path('users/', include('users.urls')),
   # 根路径重定向到博客
   path('', include('blog.urls')),
]

5 基础模板

5.1 博客首页模板设计

(templates/base.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}我的博客{% endblock %}</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding-top: 60px;
            background-color: #f8f9fa;
        }
        .navbar-brand {
            font-weight: bold;
        }
        .container {
            max-width: 960px;
        }
        .messages {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
        <div class="container">
            <a class="navbar-brand" href="{% url 'article_list' %}">📝 我的博客</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'article_list' %}">首页</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'article_create' %}">发表文章</a>
                    </li>
                </ul>
                <ul class="navbar-nav">
                    {% if user.is_authenticated %}
                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown">
                                👤 {{ user.username }}
                            </a>
                            <div class="dropdown-menu dropdown-menu-right">
                                <a class="dropdown-item" href="{% url 'change_password' %}">修改密码</a>
                                <div class="dropdown-divider"></div>
                                <a class="dropdown-item" href="{% url 'logout' %}">退出登录</a>
                            </div>
                        </li>
                    {% else %}
                        <li class="nav-item">
                            <a class="nav-link" href="{% url 'login' %}">登录</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="{% url 'register' %}">注册</a>
                        </li>
                    {% endif %}
                </ul>
            </div>
        </div>
    </nav>

    <!-- 消息提示 -->
    <div class="container messages">
        {% if messages %}
            {% for message in messages %}
                <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
                    {{ message }}
                    <button type="button" class="close" data-dismiss="alert">&times;</button>
                </div>
            {% endfor %}
        {% endif %}
    </div>

    <!-- 主内容 -->
    <div class="container">
        {% block content %}
        {% endblock %}
    </div>

    <!-- 页脚 -->
    <footer class="container text-center text-muted mt-5 mb-3">
        <p>&copy; 2026 我的博客 - Django学习项目</p>
    </footer>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js"></script>
</body>
</html>

5.2 博文操作模板设计

(blog/templates/blog/article_list.html)

{% extends 'base.html' %}

{% block title %}文章列表{% endblock %}

{% block content %}
<div class="row">
    <div class="col-12">
        <div class="d-flex justify-content-between align-items-center mb-4">
            <h2>📚 文章列表</h2>
            <a href="{% url 'article_create' %}" class="btn btn-primary">✏️ 发表文章</a>
        </div>

        {% if articles %}
            <div class="list-group">
                {% for article in articles %}
                    <a href="{% url 'article_detail' article.pk %}" class="list-group-item list-group-item-action">
                        <div class="d-flex w-100 justify-content-between">
                            <h5 class="mb-1">{{ article.title }}</h5>
                            <small class="text-muted">{{ article.created_at|date:"Y-m-d H:i" }}</small>
                        </div>
                        <p class="mb-1 text-muted">{{ article.content|truncatechars:100 }}</p>
                        <small>作者:{{ article.author.username }}</small>
                    </a>
                {% endfor %}
            </div>
        {% else %}
            <div class="alert alert-info">
                还没有文章,<a href="{% url 'article_create' %}">赶快发表第一篇吧</a></div>
        {% endif %}
    </div>
</div>
{% endblock %}

5.3 文章详情模板设计

(blog/templates/blog.article_detail.html)

{% extends 'base.html' %}

{% block title %}{{ article.title }}{% endblock %}

{% block content %}
<article class="card">
    <div class="card-body">
        <h1 class="card-title">{{ article.title }}</h1>
        <div class="text-muted mb-3">
            <span>作者:{{ article.author.username }}</span>
            <span class="ml-3">发布时间:{{ article.created_at|date:"Y-m-d H:i:s" }}</span>
            {% if article.updated_at != article.created_at %}
                <span class="ml-3">最后更新:{{ article.updated_at|date:"Y-m-d H:i:s" }}</span>
            {% endif %}
        </div>
        <hr>
        <div class="card-text" style="line-height: 1.8;">
            {{ article.content|linebreaks }}
        </div>
        <hr>
        <a href="{% url 'article_list' %}" class="btn btn-secondary">← 返回列表</a>
    </div>
</article>
{% endblock %}

5.4 发表文章模板设计

(blog/templates/blog/article_form.html)

{% extends 'base.html' %}

{% block title %}{{ action }}文章{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header">
                <h3>{{ action }}文章</h3>
            </div>
            <div class="card-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            {{ field.label_tag }}
                            {{ field }}
                            {% if field.errors %}
                                <small class="text-danger">{{ field.errors|join:", " }}</small>
                            {% endif %}
                        </div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary btn-block">
                        ✅ 提交
                    </button>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

5.5 用户认证模板设计

(users/templates/users/login.html)

{% extends 'base.html' %}

{% block title %}用户登录{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">
                <h3>🔑 用户登录</h3>
            </div>
            <div class="card-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            {{ field.label_tag }}
                            {{ field }}
                            {% if field.errors %}
                                <small class="text-danger">{{ field.errors|join:", " }}</small>
                            {% endif %}
                        </div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary btn-block">登录</button>
                </form>
                <div class="text-center mt-3">
                    <p>还没有账号?<a href="{% url 'register' %}">立即注册</a></p>
                    <p><a href="{% url 'article_list' %}">回到首页</a></p>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}
 (users/templates/users/register.html)
{% extends 'base.html' %}

{% block title %}用户注册{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">
                <h3>📝 用户注册</h3>
            </div>
            <div class="card-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            {{ field.label_tag }}
                            {{ field }}
                            {% if field.errors %}
                                <small class="text-danger">{{ field.errors|join:", " }}</small>
                            {% endif %}
                            {% if field.help_text %}
                                <small class="form-text text-muted">{{ field.help_text }}</small>
                            {% endif %}
                        </div>
                    {% endfor %}
                    <button type="submit" class="btn btn-success btn-block">注册</button>
                </form>
                <div class="text-center mt-3">
                    <p>已有账号?<a href="{% url 'login' %}">立即登录</a></p>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

5.6 修改密码模板设计

(users/templates/users/change_password.html)

{% extends 'base.html' %}

{% block title %}修改密码{% endblock %}

{% block content %}
<div class="row justify-content-center">
   <div class="col-md-6">
       <div class="card">
           <div class="card-header">
               <h3>🔒 修改密码</h3>
           </div>
           <div class="card-body">
               <form method="post" novalidate>
                   {% csrf_token %}
                   {% for field in form %}
                       <div class="form-group">
                           {{ field.label_tag }}
                           {{ field }}
                           {% if field.errors %}
                               <small class="text-danger">{{ field.errors|join:", " }}</small>
                           {% endif %}
                       </div>
                   {% endfor %}
                   <div class="alert alert-info">
                       <strong>密码要求:</strong>
                       <ul class="mb-0">
                           <li>长度在6-18个字符之间</li>
                           <li>新密码不能与原密码相同</li>
                       </ul>
                   </div>
                   <button type="submit" class="btn btn-warning btn-block">修改密码</button>
               </form>
           </div>
       </div>
   </div>
</div>
{% endblock %}

6 创建超级管理员

用户名:admins
电子邮件地址:24365808398@qq.com
密码:11111111
在这里插入图片描述

7运行以及测试

7.1 运行

python manage.py runserver
博文网址:http://127.0.0.1:8000/
后台管理:http://127.0.0.1:8000/admin/

7.2 操控页面展现

博客的首页
发表文章发表成功
文章列表

7.3 数据库管护

在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐