前面在第7节讲解利用管理后台创建基础数据时,讲到了用户登录。这里我们自己定义登录和注册页面。自定义登录、注册函数。
from django import forms
from .models import User
from django.core.exceptions import ValidationError
# Login表单
class LoginForm(forms.Form):
username = forms.fields.CharField(
label='用户名',
required=True,
min_length=3,
max_length=50,
error_messages={
"required":"用户名不可以为空!",
"min_length":"用户名不能低于3位!",
"max_length":"用户名不能超过50位!"
}
)
password = forms.fields.CharField(
label='密码',
required=True,
widget=forms.PasswordInput(),
error_messages={
"required":"密码不可以空",
}
)
remember = forms.fields.BooleanField(
required=False,
label='使用系统默认的session过期时间'
)
from django.shortcuts import render
# Create your views here.
from django.http import JsonResponse
from .forms import RegisterForm,LoginForm
from django.contrib.auth import authenticate,login
# 导入获取自定义User对象的方法
from django.contrib.auth import get_user_model
User = get_user_model() # 获取自定义User模型
# 登录视图名称不能起成login,与自带login函数重名
def loginView(request):
#初始化返回信息
context ={}
if request.method == "GET":
context['code'] =''
context['message'] = '不应该用GET方法,应该用POST方法'
context['error'] = ''
else:
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
'''
因为前端checkbox若选择,则传上来的'on',所以要进行处理
'''
if request.POST.get("remember")=='on':
remember = 1
else:
remember = 0
# 使用authenticate进行登录验证,验证成功会返回一个user对象,失败则返回None。
# 使用authenticate验证时如果is_active为False也会返回None,导致无法判断激活状态,
# 此时可以在seetings中配置:
# AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
user = authenticate(request,username=username,password=password)
# 如果验证成功且用户已激活执行下面代码
if user and user.is_active:
# 使用自带的login函数进行登录,会自动添加session信息
login(request,user)
# 自定义session,login函数添加的session不满足时可以增加自定义的session信息。
request.session["username"] = username
if remember:
request.session.set_expiry(None) # 设置session过期时间,None表示使用系统默认的过期时间
else:
request.session.set_expiry(0) # 0代表关闭浏览器session失效
context['code'] = 200
context['message'] = "验证通过"
context['error'] = ''
return JsonResponse({"code": 200,"message":"验证通过","data":{ "error":""}})
elif user and not user.is_active:
context['code'] = 400
context['message'] = "用户未激活"
context['error'] = '该用户还没有激活,请联系系统管理员激活'
else:
context['code'] = 400
context['message'] = "验证失败"
context['error'] = '用户名或密码错误'
else:
context['code'] = 400
context['message'] = "用户名或密码格式错误"
context['error'] = '用户名或密码错误'
context['form'] = LoginForm()
print(LoginForm())
return render(request,'login.html',context)
在templates/users/目录下新建应用的父模板文件base.html,在
中引入bootstrap、jquery、Font Awesome。DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="/static/plugins/bootstrap-4.5.3-dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/plugins/bootstrap-4.5.3-dist/fontawesome-free-6.4.0-web/css/all.css">
<script type="text/javascript" src="/static/js/jquery-3.5.1/jquery-3.5.1.min.js">script>
<script src="/static/plugins/bootstrap-4.5.3-dist/js/bootstrap.bundle.min.js">script>
<script src="/static/plugins/bootstrap-4.5.3-dist/js/bootstrap.min.js">script>
<script src="/static/js/utility.js">script>
<title>讲座签到系统title>
head>
<body>
<div class="container-liquid">
{# 子模板要替换的主体块 名为mainBody #}
{% block mainBody %}
{% endblock %}
div>
body>
html>
模板中表单的各输入项,是参照LoginForm表单的输入而来的(具体是复制在视图函数loginView()中,向控制台输出了print(LoginForm())的结果)
{% extends 'users/base.html' %}
{% block mainBody %}
<div class="container">
<script>
console.log('message ='+"{{message}}")
console.log('error ='+"{{error}}")
script>
<div style="width: 600px;height: 50px;">div>
<div class='text-white text-center bg-info' style="width: 600px;height: 10px;">div>
<div class='text-white text-center bg-info' style="width: 600px;height: 40px;">
<h4>登录界面h4>
div>
<div class='bg-light' style="width: 600px; height: 30px ;">div>
<div class='bg-light' style="width: 600px; ">
<div class="container" style="width: 400px; height: 280px;" >
<form action={% url "login-url" %} method="post">
{% csrf_token %}
<h5>
<div class="form-group">
<label for="id_username">用户名:label>
<input type="text" class="form-control" name="username" maxlength="50" minlength="3" required id="id_username">
div>
<div class="form-group">
<label for="id_password">密码:label>
<input type="password" class="form-control" name="password" required id="id_password">
div>
<div class="form-group">
<label for="id_remember">使用系统默认的session过期时间:label>
<input type="checkbox" name="remember" id="id_remember">
div>
<input type="submit" class="btn btn-info btn-lg" value="登录">
<a href="#" class="btn btn-info btn-lg" role="button">注册a>
h5>
form>
div>
{% if code == 400 %}
<div class="container" style="width: 400px;" id="warning">
<div class="alert alert-warning">
<p><strong>{{message}}!strong>p>
div>
div>
<script>
function remove() {
document.getElementById('warning').remove()
}
setTimeout(remove, 5000)
script>
{% endif %}
div>
div>
{% endblock %}
from django.urls import path,re_path
from . import views as view
# 子路由列表
urlpatterns = [
path('query/', view.query, name='query-url'), # 分页查询
re_path(r'^query/(?P
\d+)/(?P \d+)/$',view.query), #传值路由 path('add/', view.add, name='add-url'), # 新增
path('update/',view.update, name='update-url'), # 修改
path('delete/',view.delete, name='delete-url'), # 删除
path('login/',view.loginView, name='login-url'), # 登录
]
# 注册表单
class RegisterForm(forms.Form):
username = forms.fields.CharField(
required=True,
min_length=3,
max_length=50,
error_messages={
"required":"用户名不可以为空!",
"min_length":"用户名不能低于3位!",
"max_length":"用户名不能超过50位!"
}
)
password1 = forms.fields.CharField(
required=True,
widget=forms.PasswordInput(),
min_length=6,
max_length=50,
error_messages={
"required":"密码不可以空",
"min_length": "密码不能低于6位!",
"max_length": "密码不能超过50位!"
}
)
password2 = forms.fields.CharField(
required=False,
widget=forms.PasswordInput(),
)
email = forms.fields.EmailField(
required=True,
error_messages={
"required":"邮箱不可以为空!"
},
)
def clean_password2(self):
if not self.errors.get("password1"):
if self.cleaned_data["password2"] != self.cleaned_data["password1"]:
raise ValidationError("您输入的密码不一致,请重新输入!")
return self.cleaned_data
# 用户注册
def register(request):
#初始化返回结果
context ={}
context['form'] = RegisterForm()
context['code'] = 400
context['message'] = '注册失败'
context['username_err'] = ""
context['password1_err'] = ""
context['password2_err'] = ""
context['email_err'] = ""
print(RegisterForm())
if request.method == "GET":
context['code'] = 0
context['message'] = '不应该用GET方法,应该用POST方法'
return render(request,"register.html",context)
else:
form = RegisterForm(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password1"]
email = form.cleaned_data["email"]
username_exists = User.objects.filter(username=username).exists()
# 如果用户名已经存在
if username_exists:
context['username'] = "您输入的用户名已存在!"
return render(request,"register.html",context)
else:
# 如果邮箱名已经存在
email_exists = User.objects.filter(email=email).exists()
if email_exists:
context['email_err'] = "您输入的邮箱已存在!"
return render(request,"register.html",context)
#注册通过,返回login页面
else:
print("注册通过")
User.objects.create_user(username=username,password=password,email=email)
context['code'] = 200
context['message'] = '注册通过'
return redirect('/users/login')
else:
context['username_err'] = form.errors.get("username")
context['password1_err'] = form.errors.get("password1")
context['password2_err'] = form.errors.get("password2")
context['email_err'] = form.errors.get("email")
return render(request,"register.html",context)
{% extends 'users/base.html' %}
{% block mainBody %}
<div class="container">
<div style="width: 600px;height: 50px;">div>
<div class='text-white text-center bg-info' style="width: 600px;height: 10px;">div>
<div class='text-white text-center bg-info' style="width: 600px;height: 40px;">
<h4>注册界面h4>
div>
<div class='bg-light' style="width: 600px; height: 30px ;">div>
<div class='bg-light' style="width: 600px; ">
<div class="container" style="width: 400px; height: 410px;" >
<form action={% url "register-url" %} method="post">
{% csrf_token %}
<h5>
<div class="form-group">
<label for="id_username">用户名:label>
<input type="text" class="form-control" name="username" maxlength="50" minlength="3" required id="id_username">
div>
<div class="form-group">
<label for="id_password">电子邮箱:label>
<input type="email" class="form-control" name="email" required id="id_email">
div>
<div class="form-group">
<label for="id_password1">收入密码:label>
<input type="password" class="form-control" name="password1" maxlength="50" minlength="6" required id="id_password1">
div>
<div class="form-group">
<label for="id_password2">再次输入密码:label>
<input type="password" class="form-control" name="password2" required id="id_password2">
div>
<input type="submit" class="btn btn-info btn-lg" value="注册">
<a href={% url "login-url" %} class="btn btn-info btn-lg" role="button">返回a>
h5>
form>
div>
{% if code == 400 %}
<div class="container" style="width: 400px;" id="warning">
<div class="alert alert-warning">
<p><strong>{{message}}!strong>p>
<script>
/* 后端的空字符串到前端来变成了None 处理*/
if ("{{username_err}}"=='None'){"{{username_err}}"=''}
if ("{{password1_err}}"=='None'){"{{password1_err}}"=''}
if ("{{password2_err}}"=='None'){"{{password2_err}}"=''}
if ("{{email_err}}"=='None'){"{{email_err}}"=''}
script>
<p>原因:{{username_err}}{{password1_err}}{{password2_err}}{{email_err}}p>
div>
div>
<script>
function remove() {
document.getElementById('warning').remove()
}
setTimeout(remove, 5000)
script>
{% endif %}
div>
div>
div>
{% endblock %}
from django.urls import path,re_path
from . import views as view
# 子路由列表
urlpatterns = [
path('query/', view.query, name='query-url'), # 分页查询
re_path(r'^query/(?P
\d+)/(?P \d+)/$',view.query), #传值路由 path('add/', view.add, name='add-url'), # 新增
path('update/',view.update, name='update-url'), # 修改
path('delete/',view.delete, name='delete-url'), # 删除
path('login/',view.loginView, name='login-url'), # 登录
path('register/',view.register, name='register-url'), # 注册
]