开始做 MVP v2:用户系统 + 文章作者关联 + 登录用户发布文章。
这一版目标是:
用户可以注册
用户可以登录
用户可以登出
文章可以关联作者
只有登录用户可以发布文章
文章列表和详情页显示作者
暂时不做:
编辑文章
删除文章
评论系统
搜索
分类标签
复杂权限
第 1 步:创建 accounts 应用
在项目根目录执行:
python manage.py startapp accounts
现在会多一个目录:
accounts
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── views.py
└── migrations
然后打开 config/settings.py,在 INSTALLED_APPS 里加入:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog",
"accounts",
]
作用是告诉 Django:
这个项目现在多了一个 accounts 用户模块,请加载它。
第 2 步:配置登录相关跳转
在 config/settings.py 底部加入:
LOGIN_URL = "accounts:login"
LOGIN_REDIRECT_URL = "home"
LOGOUT_REDIRECT_URL = "home"
含义:
LOGIN_URL:未登录用户访问受保护页面时,跳到哪里登录
LOGIN_REDIRECT_URL:登录成功后跳到哪里
LOGOUT_REDIRECT_URL:登出后跳到哪里
第 3 步:创建注册表单
在 accounts 目录下新建:
accounts/forms.py
写入:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class RegisterForm(UserCreationForm):
email = forms.EmailField(
required=False,
label="邮箱",
help_text="选填。以后可以用于找回密码。"
)
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
这里使用的是 Django 内置的 UserCreationForm。
它会自动处理:
用户名
密码
二次确认密码
密码强度校验
密码加密存储
你不用自己写密码加密逻辑。
第 4 步:写注册、登出视图
打开 accounts/views.py,写入:
from django.contrib import messages
from django.contrib.auth import login, logout
from django.shortcuts import redirect, render
from django.views.decorators.http import require_POST
from .forms import RegisterForm
def register_view(request):
if request.method == "POST":
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "注册成功,已自动登录。")
return redirect("home")
else:
form = RegisterForm()
return render(request, "accounts/register.html", {"form": form})
@require_POST
def logout_view(request):
logout(request)
messages.success(request, "你已成功登出。")
return redirect("home")
这里做了两件事:
register_view:处理用户注册
logout_view:处理用户登出
注意登出使用 POST,不是普通链接跳转,这更安全。
第 5 步:创建 accounts 路由
在 accounts 目录下新建:
accounts/urls.py
写入:
from django.contrib.auth.views import LoginView
from django.urls import path
from . import views
app_name = "accounts"
urlpatterns = [
path("register/", views.register_view, name="register"),
path("login/", LoginView.as_view(template_name="accounts/login.html"), name="login"),
path("logout/", views.logout_view, name="logout"),
]
这里登录使用 Django 内置的 LoginView。
第 6 步:接入总路由
打开 config/urls.py,改成类似这样:
from django.contrib import admin
from django.urls import include, path
from blog import views
urlpatterns = [
path("admin/", admin.site.urls),
path("", views.home, name="home"),
path("blog/", include("blog.urls")),
path("accounts/", include("accounts.urls")),
]
现在会有这些地址:
/accounts/register/ 注册
/accounts/login/ 登录
/accounts/logout/ 登出,POST 请求
第 7 步:创建注册和登录模板
在 templates 下创建目录:
templates/accounts
创建:
templates/accounts/register.html
写入:
{% extends "base.html" %}
{% block title %}注册 - 我的 Django 博客{% endblock %}
{% block content %}
<section class="form-page">
<h1>注册账号</h1>
<form method="post" class="form-card">
{% csrf_token %}
{{ form.as_p }}
<button class="button" type="submit">注册</button>
</form>
<p>
已有账号?
<a href="{% url 'accounts:login' %}">去登录</a>
</p>
</section>
{% endblock %}
再创建:
templates/accounts/login.html
写入:
{% extends "base.html" %}
{% block title %}登录 - 我的 Django 博客{% endblock %}
{% block content %}
<section class="form-page">
<h1>登录</h1>
<form method="post" class="form-card">
{% csrf_token %}
{{ form.as_p }}
<button class="button" type="submit">登录</button>
</form>
<p>
没有账号?
<a href="{% url 'accounts:register' %}">去注册</a>
</p>
</section>
{% endblock %}
第 8 步:修改导航栏,显示登录状态
打开 templates/base.html,找到导航栏部分,改成类似这样:
<nav class="nav">
<a href="{% url 'home' %}">首页</a>
<a href="{% url 'blog:post_list' %}">博客</a>
{% if user.is_authenticated %}
<a href="{% url 'blog:post_create' %}">发布文章</a>
<span class="nav-user">你好,{{ user.username }}</span>
<form method="post" action="{% url 'accounts:logout' %}" class="logout-form">
{% csrf_token %}
<button type="submit">登出</button>
</form>
{% else %}
<a href="{% url 'accounts:login' %}">登录</a>
<a href="{% url 'accounts:register' %}">注册</a>
{% endif %}
<a href="/admin/">后台</a>
</nav>
这个逻辑是:
如果用户已登录:
显示用户名、发布文章、登出
如果用户未登录:
显示登录、注册
第 9 步:给 Post 添加 author 作者字段
打开 blog/models.py。
先在顶部加入:
from django.conf import settings
然后在 Post 模型里加入:
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="posts",
verbose_name="作者",
null=True,
blank=True,
)
完整结构大概变成:
from django.conf import settings
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField("标题", max_length=200)
slug = models.SlugField("URL 标识", unique=True)
summary = models.TextField("摘要", blank=True)
content = models.TextField("正文 Markdown")
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="posts",
verbose_name="作者",
null=True,
blank=True,
)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
updated_at = models.DateTimeField("更新时间", auto_now=True)
is_published = models.BooleanField("是否发布", default=True)
class Meta:
ordering = ["-created_at"]
verbose_name = "文章"
verbose_name_plural = "文章"
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("blog:post_detail", kwargs={"slug": self.slug})
这里暂时使用:
null=True
blank=True
原因是你 MVP v1 里可能已经有旧文章。旧文章没有作者,如果直接强制作者不能为空,迁移时会要求你给旧数据指定作者。初学阶段先允许旧文章作者为空,后面再慢慢规范。
第 10 步:迁移数据库
因为你修改了 models.py,所以要执行:
python manage.py makemigrations
python manage.py migrate
这一步会给数据库里的 blog_post 表增加一个 author_id 字段。
第 11 步:更新 Admin 后台显示作者
打开 blog/admin.py,把 author 加入列表:
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ("title", "author", "slug", "is_published", "created_at")
list_filter = ("is_published", "created_at", "author")
search_fields = ("title", "summary", "content")
prepopulated_fields = {"slug": ("title",)}
这样后台文章列表会多显示一列作者。
第 12 步:创建文章发布表单
在 blog 目录下新建:
blog/forms.py
写入:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "slug", "summary", "content", "is_published"]
widgets = {
"summary": forms.Textarea(attrs={"rows": 4}),
"content": forms.Textarea(attrs={"rows": 12}),
}
注意这里没有把 author 放进表单。
原因是:
作者不应该让用户自己选;
作者应该由当前登录用户自动绑定。
也就是后面这句:
post.author = request.user
第 13 步:添加发布文章视图
打开 blog/views.py,加入这些 import:
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
你原来可能已经有:
from django.shortcuts import get_object_or_404, render
改成包含 redirect 即可。
再加入:
from .forms import PostForm
然后添加发布文章视图:
@login_required
def post_create(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
messages.success(request, "文章发布成功。")
return redirect(post.get_absolute_url())
else:
form = PostForm()
return render(
request,
"blog/post_form.html",
{
"form": form,
"page_title": "发布文章",
},
)
这段代码的核心是:
@login_required
表示:
只有登录用户才能访问发布文章页面。
以及:
post.author = request.user
表示:
当前登录用户就是这篇文章的作者。
第 14 步:修改 blog 路由
打开 blog/urls.py。
注意:create/ 要放在 <slug:slug>/ 前面,否则 Django 可能会把 create 当成文章 slug。
写成:
from django.urls import path
from . import views
app_name = "blog"
urlpatterns = [
path("", views.post_list, name="post_list"),
path("create/", views.post_create, name="post_create"),
path("<slug:slug>/", views.post_detail, name="post_detail"),
]
现在新增地址:
/blog/create/
第 15 步:创建文章发布模板
创建:
templates/blog/post_form.html
写入:
{% extends "base.html" %}
{% block title %}{{ page_title }} - 我的 Django 博客{% endblock %}
{% block content %}
<section class="form-page">
<h1>{{ page_title }}</h1>
<form method="post" class="form-card">
{% csrf_token %}
{{ form.as_p }}
<button class="button" type="submit">保存文章</button>
</form>
<p>
<a href="{% url 'blog:post_list' %}">← 返回博客列表</a>
</p>
</section>
{% endblock %}
第 16 步:文章列表显示作者
打开 templates/blog/post_list.html。
在文章卡片中加上作者显示,例如:
<p class="post-date">
{{ post.created_at|date:"Y-m-d" }}
{% if post.author %}
· 作者:{{ post.author.username }}
{% else %}
· 作者:未设置
{% endif %}
</p>
你的文章列表里大概变成:
{% for post in posts %}
<article class="post-card">
<p class="post-date">
{{ post.created_at|date:"Y-m-d" }}
{% if post.author %}
· 作者:{{ post.author.username }}
{% else %}
· 作者:未设置
{% endif %}
</p>
<h2>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h2>
<p>{{ post.summary }}</p>
<a href="{{ post.get_absolute_url }}">阅读全文 →</a>
</article>
{% endfor %}
第 17 步:文章详情显示作者
打开 templates/blog/post_detail.html。
在标题下面加入:
<p class="post-date">
{{ post.created_at|date:"Y-m-d H:i" }}
{% if post.author %}
· 作者:{{ post.author.username }}
{% else %}
· 作者:未设置
{% endif %}
</p>
第 18 步:base.html 加 messages 提示
Django 的 messages.success() 需要模板显示。
打开 templates/base.html,在 <main> 里、block content 前加:
<main class="container main-content">
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="message {{ message.tags }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% block content %}{% endblock %}
</main>
第 19 步:补充 CSS
打开 static/css/style.css,底部追加:
.nav-user {
color: #4b5563;
font-size: 14px;
}
.logout-form {
margin: 0;
}
.logout-form button {
border: none;
background: transparent;
color: #2563eb;
cursor: pointer;
font: inherit;
padding: 0;
}
.logout-form button:hover {
text-decoration: underline;
}
.form-page {
max-width: 760px;
margin: 0 auto;
}
.form-card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 28px;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}
.form-card p {
display: grid;
gap: 6px;
margin-bottom: 18px;
}
.form-card input,
.form-card textarea,
.form-card select {
width: 100%;
padding: 10px 12px;
border: 1px solid #d1d5db;
border-radius: 10px;
font: inherit;
}
.form-card input:focus,
.form-card textarea:focus {
outline: 2px solid #bfdbfe;
border-color: #2563eb;
}
.helptext {
color: #6b7280;
font-size: 13px;
}
.errorlist {
color: #dc2626;
margin: 0 0 8px;
padding-left: 20px;
}
.messages {
margin-bottom: 24px;
}
.message {
padding: 12px 16px;
border-radius: 10px;
margin-bottom: 10px;
background: #eff6ff;
color: #1d4ed8;
border: 1px solid #bfdbfe;
}
.message.success {
background: #ecfdf5;
color: #047857;
border-color: #a7f3d0;
}
.message.error {
background: #fef2f2;
color: #b91c1c;
border-color: #fecaca;
}
第 20 步:启动测试
运行:
python manage.py runserver
测试这些页面:
http://127.0.0.1:8000/accounts/register/
http://127.0.0.1:8000/accounts/login/
http://127.0.0.1:8000/blog/create/
http://127.0.0.1:8000/blog/
测试流程:
1. 注册一个普通用户
2. 注册成功后应该自动登录
3. 点击“发布文章”
4. 填写标题、slug、摘要、正文 Markdown
5. 保存文章
6. 自动跳转文章详情页
7. 详情页显示作者
8. 博客列表显示作者
9. 点击登出
10. 再访问 /blog/create/,应该跳转登录页
MVP v2 完成标准
完成后你应该具备:
用户可以注册
用户可以登录
用户可以登出
登录状态会显示在导航栏
未登录用户不能发布文章
登录用户可以发布文章
新文章会自动绑定作者
文章列表显示作者
文章详情显示作者
Admin 后台可以看到文章作者
完成后建议提交版本
测试没问题后提交:
git add .
git commit -m "complete blog mvp v2 user system and post author"
这一版完成后,下一版就是:
MVP v3:文章编辑、删除、权限控制
也就是:
作者可以编辑自己的文章
作者可以删除自己的文章
管理员可以管理所有文章
普通用户不能改别人的文章
评论
qwuwepzkzojrgjwvohsipnwmtjrgkj
wjddpwrnypzttnegngdxstvwtuejld
发表评论