前言
学无止境,无止境学。大家好,我是张大鹏,之前在抖音有5万多粉丝,不过现在不拍视频,专心写公众号了。笔者目前是高级Python工程师,之前是全栈工程师,主要擅长Golang和Python开发,对于Java,Vue,React也有一些研究。工作之余,喜欢学习和分享,希望能够通过此公众号”张大鹏520″,将自己学到的东西分享给大家,和大家一起交流,一起成长,一起进步。
今天要给大家分享的是《Django加Vue电商项目实战18 实现用户注册登录和注销》,这是一个系列的教程,从零基础到项目实战。在本教程中,我会给大家介绍实现用户注册,登录和注销。比如注册页面的实现,登录页面的实现,注册和登录表单,视图函数实现等。还会配套相关的练习,大家学完以后可以自行通过练习题巩固和加深对知识点的理解。
如果大家需要本教程的PDF电子书或者完整的源码,可以在文末找到获取方式哈。
效果预览
注册页面
展示注册页面:
注册信息校验,会回显错误信息:
登录页面
展示登录页面:
登录信息校验,会回显错误信息:
登录成功以后跳转到首页:
注销功能
点击注销以后清空用户session,跳转到登录页面。
页面实现
注册页面
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>大鹏商城后台管理系统</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<link rel="shortcut icon" href="{% static 'images/favicon.ico' %}"/>
<style>
.form-group label {
color: red;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth">
<div class="row flex-grow">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left p-5">
<div class="brand-logo">
<img src="{% static 'images/logo.svg' %}">
</div>
<h4>注册新用户</h4>
<h6 class="font-weight-light">只需要非常简单的步骤,就能够快速注册一个新的账号</h6>
<form class="pt-3" method="post" action="{% url 'user:register' %}" novalidate>
{% csrf_token %}
<div class="form-group">
{{ register_form.username }}
{% if register_form.errors.username %}
<label>{{ register_form.errors.username.0 }}</label>
{% endif %}
</div>
<div class="form-group">
{{ register_form.password }}
{% if register_form.errors.password %}
<label>{{ register_form.errors.password.0 }}</label>
{% endif %}
</div>
<div class="form-group">
{{ register_form.re_password }}
{% if register_form.errors.re_password %}
<label>{{ register_form.errors.re_password.0 }}</label>
{% endif %}
</div>
<div class="mt-3">
<button class="btn btn-block btn-gradient-primary btn-lg font-weight-medium auth-form-btn"
type="submit">
立即注册
</button>
</div>
<div class="text-center mt-4 font-weight-light"> 已经拥有账号? <a
href="{% url 'user:login' %}" class="text-primary">立即登录</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
登录页面
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>大鹏商城后台管理系统</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<link rel="shortcut icon" href="{% static 'images/favicon.ico' %}"/>
<style>
.form-group label {
color: red;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth">
<div class="row flex-grow">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left p-5">
<div class="brand-logo">
<img src="{% static 'images/logo.svg' %}">
</div>
<h4>欢迎使用大鹏商城后台管理系统</h4>
<h6 class="font-weight-light">请登录您的账号</h6>
<form class="pt-3" method="post" action="{% url 'user:login' %}" novalidate>
{% csrf_token %}
<div class="form-group">
{{ login_form.username }}
{% if login_form.errors.username %}
<label>{{ login_form.errors.username.0 }}</label>
{% endif %}
</div>
<div class="form-group">
{{ login_form.password }}
{% if login_form.errors.password %}
<label>{{ login_form.errors.password.0 }}</label>
{% endif %}
{% if info %}
<label>{{ info }}</label>
{% endif %}
</div>
<div class="mt-3">
<button class="btn btn-block btn-gradient-primary btn-lg font-weight-medium auth-form-btn"
type="submit">
立即登录
</button>
</div>
<div class="text-center mt-4 font-weight-light"> 没有账号? <a
href="{% url 'user:register' %}" class="text-primary">立即注册</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
公共顶部实现注销
{% load static %}
{#顶部导航开始#}
<nav class="navbar default-layout-navbar col-lg-12 col-12 p-0 fixed-top d-flex flex-row">
{#logo#}
<div class="text-center navbar-brand-wrapper d-flex align-items-center justify-content-center">
<a class="navbar-brand brand-logo" href="{% url 'index:index' %}">
<img src="{% static 'images/logo.svg' %}" alt="logo"/>
</a>
<a class="navbar-brand brand-logo-mini" href="{% url 'index:index' %}">
<img src="{% static 'images/logo-mini.svg' %}" alt="logo"/>
</a>
</div>
{#导航菜单#}
<div class="navbar-menu-wrapper d-flex align-items-stretch">
<button class="navbar-toggler navbar-toggler align-self-center" type="button" data-toggle="minimize">
<span class="mdi mdi-menu"></span>
</button>
{#搜索#}
<div class="search-field d-none d-md-block">
<form class="d-flex align-items-center h-100" action="#">
<div class="input-group">
<div class="input-group-prepend bg-transparent">
<i class="input-group-text border-0 mdi mdi-magnify"></i>
</div>
<input type="text" class="form-control bg-transparent border-0" placeholder="请输入...">
</div>
</form>
</div>
{#右侧菜单#}
<ul class="navbar-nav navbar-nav-right">
{#头像#}
<li class="nav-item nav-profile dropdown">
<a class="nav-link dropdown-toggle" id="profileDropdown" href="#" data-bs-toggle="dropdown"
aria-expanded="false">
<div class="nav-profile-img">
<img src="{% static 'images/faces/face1.jpg' %}" alt="image">
<span class="availability-status online"></span>
</div>
<div class="nav-profile-text">
<p class="mb-1 text-black">{{ request.user.username }}</p>
</div>
</a>
<div class="dropdown-menu navbar-dropdown" aria-labelledby="profileDropdown">
<a class="dropdown-item" href="#">
<i class="mdi mdi-cached me-2 text-success"></i> 用户日志
</a>
<div class="dropdown-divider"></div>
{#注销#}
<a class="dropdown-item" href="{% url 'user:logout' %}">
<i class="mdi mdi-logout me-2 text-primary"></i> 注销
</a>
</div>
</li>
{#全屏#}
<li class="nav-item d-none d-lg-block full-screen-link">
<a class="nav-link">
<i class="mdi mdi-fullscreen" id="fullscreen-button"></i>
</a>
</li>
{#消息#}
<li class="nav-item dropdown">
<a class="nav-link count-indicator dropdown-toggle" id="messageDropdown" href="#"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="mdi mdi-email-outline"></i>
<span class="count-symbol bg-warning"></span>
</a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown preview-list"
aria-labelledby="messageDropdown">
<h6 class="p-3 mb-0">消息列表</h6>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<img src="{% static 'images/faces/face4.jpg' %}" alt="image" class="profile-pic">
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject ellipsis mb-1 font-weight-normal">标记并发送你的消息</h6>
<p class="text-gray mb-0"> 1 分钟以前 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<img src="{% static 'images/faces/face2.jpg' %}" alt="image" class="profile-pic">
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject ellipsis mb-1 font-weight-normal">标记并发送你的消息</h6>
<p class="text-gray mb-0"> 15 分钟以前 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<img src="{% static 'images/faces/face3.jpg' %}" alt="image" class="profile-pic">
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject ellipsis mb-1 font-weight-normal">更新你的头像</h6>
<p class="text-gray mb-0"> 18 分钟以前 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<h6 class="p-3 mb-0 text-center">4 条新消息</h6>
</div>
</li>
{#通知#}
<li class="nav-item dropdown">
<a class="nav-link count-indicator dropdown-toggle" id="notificationDropdown" href="#"
data-bs-toggle="dropdown">
<i class="mdi mdi-bell-outline"></i>
<span class="count-symbol bg-danger"></span>
</a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown preview-list"
aria-labelledby="notificationDropdown">
<h6 class="p-3 mb-0">通知列表</h6>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<div class="preview-icon bg-success">
<i class="mdi mdi-calendar"></i>
</div>
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject font-weight-normal mb-1">今日提现</h6>
<p class="text-gray ellipsis mb-0"> 记得学习Django哦 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<div class="preview-icon bg-warning">
<i class="mdi mdi-settings"></i>
</div>
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject font-weight-normal mb-1">设置</h6>
<p class="text-gray ellipsis mb-0"> 更新面板 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item preview-item">
<div class="preview-thumbnail">
<div class="preview-icon bg-info">
<i class="mdi mdi-link-variant"></i>
</div>
</div>
<div class="preview-item-content d-flex align-items-start flex-column justify-content-center">
<h6 class="preview-subject font-weight-normal mb-1">启动后台</h6>
<p class="text-gray ellipsis mb-0"> 新的后台管理系统 </p>
</div>
</a>
<div class="dropdown-divider"></div>
<h6 class="p-3 mb-0 text-center">查看所有通知</h6>
</div>
</li>
{#图标#}
<li class="nav-item nav-logout d-none d-lg-block">
<a class="nav-link" href="#">
<i class="mdi mdi-power"></i>
</a>
</li>
{#图标#}
<li class="nav-item nav-settings d-none d-lg-block">
<a class="nav-link" href="#">
<i class="mdi mdi-format-line-spacing"></i>
</a>
</li>
</ul>
<button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button"
data-toggle="offcanvas">
<span class="mdi mdi-menu"></span>
</button>
</div>
</nav>
{#顶部导航结束#}
功能实现
路由实现
from django.urls import path
from . import views
app_name = 'user'
urlpatterns = [
# 用户注册
path('register/', views.register, name='register'),
# 用户登录
path('login/', views.login_view, name='login'),
# 用户注销
path('logout/', views.logout_view, name='logout'),
]
表单实现
import re
from django import forms
from django.core.exceptions import ValidationError
def mobile_validate(value):
"""手机号校验器"""
mobile_re = re.compile(
r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
class LoginForm(forms.Form):
username = forms.CharField(label="用户名", min_length=6,
widget=forms.widgets.TextInput(
# 其中class样式为form-control,这是bootstrap的样式
attrs={'class': 'form-control form-control-lg', 'placeholder': "请输入用户名"}),
error_messages={
'required': '用户姓名不能为空',
'min_length': '长度最少6位',
})
password = forms.CharField(label="密码", min_length=6, max_length=72,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control form-control-lg", 'placeholder': "请输入密码"},
render_value=True),
error_messages={
'max_length': '密码最长72位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
class RegisterForm(LoginForm):
re_password = forms.CharField(label="确认密码", min_length=6, max_length=72,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control form-control-lg", 'placeholder': "请确认密码"},
render_value=True),
error_messages={
'max_length': '密码最长72位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
def clean(self):
"""当表单校验的时候,自动触发"""
# 获取密码和确认密码
password = self.cleaned_data.get("password")
re_password = self.cleaned_data.get("re_password")
# 判断两个密码是否一致
if password != re_password:
self.add_error("re_password", ValidationError("二次密码输入不一致"))
class UserRegForm1(forms.Form):
username = forms.CharField(label="用户名", min_length=6,
widget=forms.widgets.TextInput(
# 其中class样式为form-control,这是bootstrap的样式
attrs={'class': 'form-control', 'placeholder': "请输入用户名"}),
error_messages={
'required': '用户姓名不能为空',
'min_length': '长度最少6位',
})
password = forms.CharField(label="密码", min_length=6, max_length=72,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control"}, render_value=True),
error_messages={
'max_length': '密码最长72位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
re_password = forms.CharField(label="确认密码", min_length=6, max_length=72,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control"}, render_value=True),
error_messages={
'max_length': '密码最长72位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
nickname = forms.CharField(label="昵称", max_length=20, required=False,
widget=forms.widgets.TextInput(
# 其中class样式为form-control,这是bootstrap的样式
attrs={'class': 'form-control', 'placeholder': "请输入用户昵称"}),
error_messages={
'required': '用户昵称不能为空',
'max_length': '昵称长度不能超过20位',
})
email = forms.EmailField(label="邮箱",
widget=forms.widgets.EmailInput(
attrs={'class': 'form-control', }),
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不对',
})
mobile = forms.CharField(label="手机号码", validators=[mobile_validate],
widget=forms.widgets.TextInput(
attrs={'class': 'form-control', }),
error_messages={
'required': '手机号码不能为空',
})
user_img = forms.ImageField(label="用户头像", required=False, widget=forms.widgets.FileInput(
attrs={'class': 'form-control'}))
def clean(self):
"""当表单校验的时候,自动触发"""
# 获取密码和确认密码
password = self.cleaned_data.get("password")
re_password = self.cleaned_data.get("re_password")
# 判断两个密码是否一致
if password != re_password:
self.add_error("re_password", ValidationError("二次密码输入不一致"))
视图函数
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from django.urls import reverse
from users.models import User
from .forms import LoginForm, RegisterForm
def register(request):
"""用户注册"""
# 渲染注册页面
if request.method == "GET":
return render(request, 'apps/users/register.html', {"register_form": RegisterForm()})
# 执行注册功能
if request.method == "POST":
# 获取用户名和密码
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 判断是否已存在
username = request.POST.get("username", '')
users = User.objects.filter(username=username)
if users:
return render(request, 'apps/users/register.html',
{"register_form": RegisterForm(), "msg": "该用户已存在"})
# 注册用户
# 移除重复密码
register_form.cleaned_data.pop("re_password")
# 添加数据
# 是否登录后台
register_form.cleaned_data["is_staff"] = 0
# 是否为超级管理员
register_form.cleaned_data["is_superuser"] = 0
# 是否激活
register_form.cleaned_data["is_active"] = 1
# 新增
User.objects.create_user(**register_form.cleaned_data)
# 跳转到登陆页面
return redirect(reverse("user:login"))
# 返回错误信息
return render(request, 'apps/users/register.html',
{"register_form": register_form})
def login_view(request):
"""用户登录"""
# 渲染登录页面
if request.method == "GET":
return render(request, 'apps/users/login.html', {"login_form": LoginForm})
# 执行登录功能
if request.method == "POST":
# 校验登录
login_form = LoginForm(request.POST)
info = ""
if login_form.is_valid():
# 获取用户名和密码
username = request.POST.get("username", '')
password = request.POST.get("password", '')
if User.objects.filter(username=username): # 判断用户是否存在
# 如果存在,进行验证
user = authenticate(username=username, password=password)
if user: # 如果验证通过
if user.is_active: # 如果用户状态为激活
login(request, user) # 进行登陆操作,完成session的设置
return redirect(reverse("index:index"))
else:
info = "用户还未激活"
else:
info = "账号密码不对,请重新输入"
else:
info = '用户账号不存在,请查询'
return render(request, 'apps/users/login.html', {"info": info, "login_form": login_form})
def logout_view(request):
"""用户注销"""
logout(request)
return redirect(reverse("user:login"))
权限中间件
import re
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
class PermissionMiddleWare(MiddlewareMixin):
"""权限校验中间件"""
def process_request(self, request):
"""处理请求的方法"""
# 获取当前路径
curr_path = request.path
# 白名单处理
white_list = [reverse("user:login"), reverse("user:register")]
for w in white_list:
if re.search(w, curr_path):
return None # 通过
# 验证是否登陆
if not request.user.is_authenticated:
return redirect(reverse("user:login"))
结语
关注我不迷路,欢迎关注我的微信公众号”张大鹏520″,如果您对此文章感兴趣,欢迎点赞收藏,留言评论。
文中所有代码,只需要打赏20元,然后留言评论“已打赏”,即可获取哦。
本文的PDF电子书版,只需要打赏3元,然后留言评论“已打赏”,即可获取哦。
写文章不容易,不喜勿喷哈,如果有想要学编程,学项目,或者在工作中有项目难以单独完成需要提供帮助的同学,欢迎私信我哈。生活不易,想要利用学到的编程知识,业余赚点零花钱。
接项目:网站开发,APP开发,各种管理系统开发。
带徒弟:Python编程教学,Golang编程教学,前端编程教学。
谢谢您!!!
原文始发于微信公众号(张大鹏520):Django加Vue电商项目实战18 实现用户注册登录和注销
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/48561.html