🚀 Django 注册功能实现全流程(分 4 步)
按照 “设计 UI → 创建视图 → 添加路由 → 前后端对接” 的四个步骤,让用户在 accounts/register/
页面完成注册~
- 设计 UI:
register.html
模板 - 创建视图
- 添加路由
- 前后端对接
第二步:创建视图(直接返回 HTML 文件)
def register(request):
# 1. 打开并读取 register.html 的内容
html = open('register.html', encoding="utf-8").read()
# 2. 把 HTML 内容返回给用户
return HttpResponse(html)
把 register.html
放到 App 的 templates
目录 ,用 Django 模板加载(更规范):
from django.shortcuts import render
def register(request):
# Django 会自动去 templates 目录找 register.html
return render(request, 'register.html')
第三步:添加路由(让 accounts/register
能访问)
项目主路由 urls.py
:
from django.urls import path, include
urlpatterns = [
# ... 其他路由 ...
path("accounts/", include("beifan.accounts_urls")),
]
- 路径清晰:注册页面的路径是
accounts/register/
,别人一看就知道是 “账号相关功能”,和业务页面(beifan/xxx
)区分开。 - 路由解耦:把 “账号相关路由” 放到单独的
accounts_urls.py
,让beifan/urls.py
专注处理业务路由,代码更清晰。 - 可扩展:后续想加登录、登出、密码重置功能,直接在
accounts_urls.py
里加路由,维护更方便。
🎭 Django 路由 “接力赛”
urlpatterns = [
# ... 其他路由 ...
path("accounts/", include("django.contrib.auth.urls")),
path("accounts/", include("beifan.accounts_urls")),
]
Django 的匹配逻辑:
- 当用户访问
accounts/xxx
时,Django 先找 第一个accounts/
路由(即django.contrib.auth.urls
)。 - 如果
django.contrib.auth.urls
里有匹配的子路由(比如accounts/login
),就用它的视图。 - 如果 没找到 ,才会找 第二个
accounts/
路由(即beifan.accounts_urls
)。
django.contrib.auth.urls
里有啥?
Django 自带的 django.contrib.auth.urls
,默认包含这些常用路由:
urlpatterns = [
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]
场景 1:访问 accounts/login/
- 用户访问
http://127.0.0.1:8000/accounts/login/
- Django 先检查第一个
accounts/
路由(django.contrib.auth.urls
)→ 发现有login/
路由 → 调用 Django 自带的登录视图 → 返回登录页面。
场景 2:访问 accounts/register/
- 用户访问
http://127.0.0.1:8000/accounts/register/
- Django 先检查第一个
accounts/
路由(django.contrib.auth.urls
)→ 发现 没有register/
路由 → 接力给第二个accounts/
路由(beifan.accounts_urls
)。 beifan.accounts_urls
里有register/
路由 → 调用你的register
视图 → 返回注册页面。
接下来需要在 beifan/accounts_urls.py
里配置子路由
from django.urls import path
from beifan import views # 导入 beifan 的 views
urlpatterns = [
# 当用户访问 accounts/register 时,调用 register 视图
path('register', views.register, name='register'),
]
路由别名的超能力(为什么需要 name
)
1. 模板里用别名生成 URL
在 HTML 模板里,不用写死 URL :
<!-- BAD :硬写 URL ,如果路径变了要改所有模板 -->
<a href="/accounts/register">去注册</a>
<!-- GOOD :用别名,路径变了只改路由配置 -->
<a href="{% url 'register' %}">去注册</a>
- 作用:如果以后把
register
的 URL 改成accounts/signup
,只要在路由里改path('signup', ...)
,模板里的{% url 'register' %}
会自动变成新路径,不用挨个改模板!
2. 视图里用别名跳转
在视图函数里,跳转页面时用别名:
from django.shortcuts import redirect
from django.urls import reverse
def login(request):
# 登录成功后,跳转到注册页(用别名)
return redirect(reverse('register'))
- 作用:和模板同理,路由路径变了,只要别名不变,
reverse('register')
会自动生成新路径,不用改视图里的跳转逻辑~
四、🚀 前后端对接!Axios 请求 & Django 交互
核心流程
前端(浏览器)用 Axios
发请求 → 后端(Django)接收并处理 → 返回响应 → 前端处理响应 / 错误~
1. 引入 Axios 库
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js"></script>
- 作用:加载
Axios
库,让前端能发 HTTP 请求(比如 POST、GET )。 - 类比:给前端装一个 “请求小飞机” ,用来给后端送信(数据)~
2. Axios 发 POST 请求
axios({
method: "post", // 请求方法:POST
url: "http://127.0.0.1:8000/accounts/register", // 后端接口地址
data: formData, // 要发的数据(比如注册表单)
}).then(function (response) { // 请求成功
alert(response.data.msg);
}).catch(function (error) { // 请求失败
alert(error.response.data.msg);
})
① method: "post"
→ 请求方法
- 作用:告诉后端 “我要提交数据”(比如注册表单)。
- 类比:给 “请求小飞机” 设置飞行模式 → “投递模式”(送数据给后端)。
② url: "http://127.0.0.1:8000/accounts/register"
→ 后端地址
- 作用:指定后端的 “收件地址” ,告诉 Axios 把请求发给 Django 的
accounts/register
接口。 - 类比:在 “请求小飞机” 上写清楚收件地址 → 飞到 Django 后端的
accounts/register
门口~
③ data: formData
→ 要发的数据
- 作用:把前端表单数据(比如用户名、密码)打包成
formData
,发给后端。 - 类比:把注册表单写成 “信件”,放进 “请求小飞机” 的货舱~
④ .then(...)
→ 请求成功的回调
- 作用:如果后端成功收到并处理请求,执行这里的代码。
response.data.msg
:假设后端返回的数据里有msg
字段(比如 “注册成功”),用alert
弹提示。- 类比:“请求小飞机” 成功送到信件,后端回信里有
msg
→ 前端弹出回信内容~
⑤ .catch(...)
→ 请求失败的回调
- 作用:如果请求失败(比如网络问题、后端报错),执行这里的代码。
error.response.data.msg
:获取后端返回的错误信息(如果有),用alert
弹提示。- 类比:“请求小飞机” 没送到,或者后端回信说 “操作失败” → 前端弹出错误原因~
后端需要做什么?(Django 部分)
接收并处理 POST 请求
在 beifan/views.py
的 register
视图里,处理前端发的 POST 数据:
import json
from django.http import JsonResponse, HttpResponse
from django.contrib.auth import login, User # 导入 User 模型和登录函数
def register(request):
"""
处理用户注册请求的视图函数,支持 GET 展示页面、POST 提交注册逻辑
"""
# 1. 处理 GET 请求:返回注册页面 HTML
if request.method == 'GET':
# 读取 HTML 文件并返回
html = open('register.html', encoding="utf-8").read()
return HttpResponse(html, status=200)
# 2. 处理 POST 请求:校验数据并完成注册
elif request.method == 'POST':
# 解析请求体的 JSON 数据
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return JsonResponse({
"code": -10,
"msg": "请求体不是合法的 JSON 格式"
}, status=400)
# 3. 校验必填字段(email、username、password、password_confirm)
key_list = ['email', 'username', 'password', 'password_confirm']
for key in key_list:
if len(data.get(key, "")) < 1:
return JsonResponse({
"code": -1,
"msg": f"{key} 不能为空"
}, status=422)
# 4. 校验两次密码是否一致
if data['password'] != data['password_confirm']:
return JsonResponse({
"code": -2,
"msg": "两次密码输入不一致"
}, status=422)
# 5. 校验密码长度(不少于 6 位)
if len(data['password']) < 6:
return JsonResponse({
"code": -3,
"msg": "密码长度不能少于 6 位"
}, status=422)
# 6. 校验用户名是否已存在
user_list = User.objects.filter(username=data['username'])
if len(user_list) > 0:
return JsonResponse({
"code": -4,
"msg": "该用户已存在"
}, status=400)
# 7. 所有校验通过,创建新用户
try:
new_user = User.objects.create_user(
username=data['username'],
email=data['email'],
password=data['password'],
)
except Exception as e:
return JsonResponse({
"code": -5,
"msg": f"创建用户失败:{str(e)}"
}, status=500)
# 8. 自动登录(注册成功后直接登录)
login(request, new_user)
# 9. 返回注册成功响应
return JsonResponse({
"code": 0,
"msg": "注册成功"
}, status=201)
# 3. 处理不支持的请求方法
else:
return JsonResponse({
"code": -99,
"msg": "不支持的请求方法"
}, status=405)
五、🎭 详解后端处理视图
整体功能 🌟
这个 register
函数就像一个 “注册小管家” ,能同时处理两种请求:
- 👉
GET
请求:给前端返回注册页面(让用户看到输入框) - 👉
POST
请求:接收前端发来的注册数据,验证、创建用户,最后告诉 “成功 / 失败”
处理 GET
请求:给前端 “展示注册页面” 📄
if request.method == 'GET':
# 读取 HTML 文件并返回
html = open('register.html', encoding="utf-8").read()
return HttpResponse(html, status=200)
场景再现:
前端用户在浏览器输入 accounts/register
→ 发 GET
请求给后端~
- 小管家说:“哦,是来要注册页面的呀!”
- 打开
register.html
文件,把内容读出来 → 用HttpResponse
打包成 “网页快递” 发给前端~ - 前端用户就能看到注册表单(输入用户名、密码的页面)啦!
处理 POST
请求:接收前端的注册数据并处理 📦
当用户填完表单点 “注册”,前端用 Axios 发 POST
请求,把数据发给后端~
小管家开始 “三步走”:解析数据 → 校验数据 → 创建用户~
① 第一步:解析前端发来的 JSON 数据 📦→📝
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return JsonResponse({
"code": -10,
"msg": "请求体不是合法的 JSON 格式"
}, status=400)
request.body
:前端用 Axios 发的data
数据(比如{username: "小明", password: "123456"}
),藏在这里~json.loads(...)
:把 JSON 字符串转换成 Python 字典data
,方便后续操作~- 万一前端发的不是 JSON(比如格式错了)→ 小管家返回
code=-10
+ “格式不对”,前端catch
到后弹窗提示用户~
② 第二步:校验数据(看看用户填的对不对) ✅❌
小管家化身 “安检员”,一个个检查数据:
🔍 检查必填项(email、username、password、password_confirm)
key_list = ['email', 'username', 'password', 'password_confirm']
for key in key_list:
if len(data.get(key, "")) < 1:
return JsonResponse({
"code": -1,
"msg": f"{key} 不能为空"
}, status=422)
- 场景:如果用户没填
username
→ 小管家返回code=-1
+ “username 不能为空”~ - 前端收到后,弹窗提示用户 “用户名不能为空”,超贴心!
🔍 检查两次密码是否一致
if data['password'] != data['password_confirm']:
return JsonResponse({
"code": -2,
"msg": "两次密码输入不一致"
}, status=422)
- 场景:用户第一次输
123456
,第二次输1234567
→ 小管家返回code=-2
+ “两次密码不一样”~
🔍 检查密码长度(不少于 6 位)
if len(data['password']) < 6:
return JsonResponse({
"code": -3,
"msg": "密码长度不能少于 6 位"
}, status=422)
- 场景:用户密码输
123
→ 小管家返回code=-3
+ “密码太短啦”~
🔍 检查用户名是否已存在
user_list = User.objects.filter(username=data['username'])
if len(user_list) > 0:
return JsonResponse({
"code": -4,
"msg": "该用户已存在"
}, status=400)
- 小管家去数据库 “查户口”:有没有叫这个用户名的用户?
- 有的话 → 返回
code=-4
+ “用户名被抢啦”~
③ 第三步:所有检查通过!创建用户并自动登录 🎉
try:
new_user = User.objects.create_user(
username=data['username'],
email=data['email'],
password=data['password'],
)
except Exception as e:
return JsonResponse({
"code": -5,
"msg": f"创建用户失败:{str(e)}"
}, status=500)
# 自动登录
login(request, new_user)
# 返回成功响应
return JsonResponse({
"code": 0,
"msg": "注册成功"
}, status=201)
User.objects.create_user(...)
:Django 自带的 “创建用户魔法”,会自动把密码加密(不会明文存!)~- 万一创建失败(比如数据库出错)→ 返回
code=-5
+ 错误原因~ - 成功的话 → 调用
login(request, new_user)
:自动登录!用户不用再手动输账号密码~ - 最后返回
code=0
+ “注册成功” → 前端then
里弹窗庆祝,比如 “恭喜注册成功!”
request
里装了啥?
当用户注册时,前端发的是 POST
请求,所以 request
里确实包含 POST 相关的信息:
request.method
→ 等于"POST"
(告诉后端这是个提交数据的请求)request.POST
或request.body
→ 装着用户填的注册数据(用户名、密码等)
但 request
里还有更多 “宝贝”:
request.user
→ 当前用户(注册前是匿名用户,登录后会变成new_user
)request.session
→ 用来存储会话信息的 “小本本”(login
函数主要用它来记录登录状态)request.META
→ 浏览器信息、IP 地址等 “隐藏信息”
login
函数拿了 request
会做什么?
login(request, new_user)
拿到这个 “快递箱” 后,主要干这两件事:
在
request.session
里记笔记
给new_user
生成一个唯一的 “登录凭证”(类似 sessionid),写进request.session
里,相当于:request.session['user_id'] = new_user.id # 偷偷偷偷记下用户ID
更新
request.user
把request.user
从 “匿名用户” 换成new_user
,这样后续在视图里用request.user
就能直接拿到登录用户啦~
处理不支持的请求方法(比如 PUT、DELETE) 🚫
else:
return JsonResponse({
"code": -99,
"msg": "不支持的请求方法"
}, status=405)
- 如果前端发了个奇怪的请求方法(比如
PUT
)→ 小管家说 “我不支持哦”,返回code=-99
~
前后端 “对话” 小剧场 🎬
场景:用户注册成功
前端(Axios):
“小管家,我发POST
请求啦,数据是{username: "小明", password: "123456", ...}
,请查收!”后端小管家:
- 解析数据 → 检查必填项 → 密码一致 → 密码够长 → 用户名没重复 → 全部通过!
- 创建用户 “小明” → 自动登录 → 回复:
{"code":0, "msg":"注册成功"}
前端:
收到code=0
→ 弹窗 “注册成功!” → 跳转到首页~
场景:用户密码太短
前端:“小管家,数据是
{password: "123", ...}
~”后端小管家:
检查发现密码长度 3 < 6 → 回复:{"code":-3, "msg":"密码长度不能少于 6 位"}
前端:
收到code=-3
→ 弹窗 “密码太短啦,至少要 6 位哦~” → 让用户重新输入~
代码超贴心的点 ❤️
- 错误码清晰:每个错误都有专属
code
(比如-1
空字段、-2
密码不一致),前端能根据code
做不同处理(比如给输入框标红)。 - 自动加密密码:
create_user
会自动加密密码,数据库里看不到明文,超安全! - 自动登录:注册成功后直接登录,用户体验 up up ~
- 支持 GET/POST:一个函数搞定 “展示页面” 和 “处理注册”,不用分开写~
六、🌈 登录成功后跳转到业务页面!前端重定向
方法 1:用 window.location.href
直接跳转 🚀
这是最直接的方式,就像在浏览器地址栏输入新网址一样~
代码示例(配合 Axios 成功回调):
// 登录请求的 Axios 代码
axios({
method: "post",
url: "http://127.0.0.1:8000/accounts/login",
data: {
username: "小明",
password: "123456"
}
}).then(function(response) {
// 登录成功(后端返回 code=0 )
if (response.data.code === 0) {
// 跳转到业务页面(比如 /beifan/index )
window.location.href = "/beifan/index";
// 也可以写全路径:http://127.0.0.1:8000/beifan/index
}
}).catch(function(error) {
// 登录失败,弹窗提示
alert(error.response.data.msg);
});
原理:
window.location.href
是浏览器的 “地址栏魔法”,赋值新路径后,页面会立刻跳转到该地址~
类比:你告诉浏览器 “别待在登录页了,快去业务页面!”,浏览器就听话跳转啦~
方法 2:用 window.location.replace
跳转(更干净)🧼
和方法 1 类似,但会 “删掉” 登录页的历史记录,用户点 “后退” 不会回到登录页~
代码示例:
.then(function(response) {
if (response.data.code === 0) {
// 跳转到业务页面,且保留登录页历史
window.location.replace("/beifan/index");
}
})
适合场景:
登录后不希望用户后退回到登录页(比如已登录状态下不允许再访问登录页),用这个更合适~
方法 3:如果用了前端框架(如 Vue/React)🧩
如果你的用了 Vue 或 React ,可以用框架自带的路由工具跳转(更优雅雅~)
Vue 示例(用 Vue Router):
// 先导入路由
import { useRouter } from 'vue-router';
const router = useRouter();
// 登录成功后
.then(function(response) {
if (response.data.code === 0) {
// 跳转到业务页面(路由配置的 path )
router.push("/beifan/index");
}
})
React 示例(用 React-router-dom):
// 先导入 useNavigate
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
// 登录成功后
.then(function(response) {
if (response.data.code === 0) {
// 跳转到业务页面
navigate("/beifan/index");
}
})
小剧场:登录端跳转全过程 🎭
- 用户填完登录表单,点击 “登录” → 前端发 Axios 请求给后端。
- 后端验证成功,返回
{"code":0, "msg":"登录成功"}
。 - 前端
then
回调里:- 看到
code=0
→ 执行window.location.href = "/beifan/index"
。 - 浏览器地址栏瞬间变成
http://127.0.0.1:8000/beifan/index
→ 页面面跳转到业务页面~
- 看到
- 用户开心地在业务务页面操作啦!