Django实现可重用注册登录系统

一、搭建项目环境

首先创建Django项目:
在这里插入图片描述
注意:虚拟环境是一个隔离的python环境,跟已有的python环境没有任何关系,这样可以保证项目完成后可以上传到私有仓库中便于重复使用

二、项目整体框架完成

1.创建app

 python manage.py startapp login

2.设置时区语言

# filename: loginRegister/settings.py
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

3.数据库表生成

python manage.py migrate           %将迁移脚本的内容写入数据库并创建数据库表
python manage.py createsuperuser   %创建后台登录的超级用户

4.启动开发服务器(两种方式)

命令启动:
python manage.py runserver 9999    %指定9999端口运行(可以不指定,默认8000端口)

配置Django Server:
在这里插入图片描述

5.测试

访问网址: http://127.0.0.1:9999/
访问网址: http://127.0.0.1:9999/admin/

6.git提交项目代码到本地仓库

git init       %初始化git目录,创建本地仓库
安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore
git add *      %添加修改到暂存区
git commit -m "搭建项目开发环境"   %将暂存区的代码提交到本地git仓库
git log        %查看历史提交记录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、设计数据库模型

1.数据库模型文件

from django.db import models

# Create your models here.

# appname_siteuser
class SiteUser(models.Model):
    """用户的数据库模型,注册/登录需要"""
    gender_choice = (
        (0, "未知"),
        (1, "男"),
        (2, "女"),
    )
    name = models.CharField(max_length=128, unique=True, verbose_name="用户名")
    password = models.CharField(max_length=256, verbose_name="密码")
    email = models.EmailField(unique=True, verbose_name="电子邮箱")
    gender = models.IntegerField(choices=gender_choice, default=0, verbose_name="性别")
    # auto_now_add=True时为添加时的时间,更新对象时不会有变动。
    # auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    modify_time = models.DateTimeField(auto_now=True, verbose_name="最后一次修改时间")
    # null针对数据库层面的, blank针对表单的
    last_login_time = models.DateTimeField(null=True, blank=True,
                                           verbose_name="最后一次登录时间")
    has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")
    def __str__(self):
        return  self.name

    class Meta:
        verbose_name = "网站用户管理"
        verbose_name_plural = verbose_name

2.设置数据库后端

Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改

3.注册app

# loginRegister/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'login',    %修改的内容
]

生成迁移脚本并写入数据库(更改models.py文件要生成迁移脚本并写入数据库):

python manage.py migrate            %将迁移脚本的内容写入数据库并创建数据库表
python manage.py createsuperuser    %创建后台登录的超级用户

4.测试是否成功

打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功

在这里插入图片描述

5.数据库模型后台管理

# login/admin.py
from django.contrib import admin
from login.models import SiteUser
# Register your models here.

# 后台管理设置的信息
class SiteUserAdmin(admin.ModelAdmin):
    list_display = ['name', 'gender', 'email']
    list_display_links = ['name']
    list_filter = ['gender', 'create_time']
    list_per_page = 10
admin.site.register(SiteUser, SiteUserAdmin)

访问网址: http://127.0.0.1:9999/admin/进行测试:
在这里插入图片描述

四、路由与视图函数框架搭建

1.路由配置

主路由配置文件

# loginRegister/urls.py

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

urlpatterns = [
     path('admin/', admin.site.urls),
     path('', include('login.urls')) # 添加的行, 如果没有前缀,访问子路由配置文件
]

子路由配置文件

# login/urls.py(新建的文件)

from django.urls import path, include
from login import views

urlpatterns = [
     path('index/', views.index, name='index'),
     path('login/', views.login, name='login'),
     path('register/', views.register, name='register'),
     path('logout/', views.logout, name='logout'),
]

2.视图函数的配置

# login/views.py
from django.shortcuts import render, redirect
# Create your views here

def index(request):
    pass
    return render(request, 'login/index.html')

def login(request):
    pass
    return render(request, 'login/login.html')

def register(request):
    pass
    return render(request, 'login/register.html')

def logout(request):
    pass
    # redirect: 重定向(跳转)
   return redirect('/login/')

3.模板template的配置

templates/login/index.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>首页</title>
</head>
<body>
<h1>这是首页的模拟界面</h1>
</body>
</html>

templates/login/login.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form>
	用户名: <input type="text" placeholder="username"><br/>
	密码: <input type="password" placeholder="password"><br/>
	<input type="submit" value="登录">
</form>
</body>
</html>

templates/login/register.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>注册界面</title>
</head>
<body>
<h1>用户注册</h1>
<form>
	用户名: <input type="text" placeholder="username"><br/>
	电子邮箱: <input type="email" placeholder="email"><br/>
	密码: <input type="password" placeholder="password"><br/>
	确认密码: <input type="password" placeholder="password"><br/>
	<input type="submit" value="注册">
</form>
</body>
</html>

4.测试

注意:添加文件后需要重新执行python manage.py runserver才能生效

访问网址: http://127.0.0.1:9999/index/
访问网址: http://127.0.0.1:9999/login/
访问网址: http://127.0.0.1:9999/register/

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.前端界面设计与优化

设计更加美观,功能更加全面的网页展示:

模板文件的完善

# templates/login/login.html

<div class="col-sm">
	<h3 style="text-align: center">用户登录</h3>
	# 修改1. 有message信息则显示, 没有就不显示。
	{
    
    % if message %}
	<div class="alert alert-warning" role="alert">
		<strong>登录失败!</strong> {
    
    {
    
     message }}
	</div>
	{
    
    % endif %}
	# 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
	<form action="/login/" method="post">
		# 修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
		{
    
    % csrf_token %}
		<div class="form-group">
		<label>用户名</label>
		# 修改4:name="username"指定表单内容存储的key值名称, eg:
{
    
    "username":"你填的用户名"}
		<input type="text" class="form-control" name="username">
	</div>
	<div class="form-group">
		<label>Password</label>
		<input type="password" class="form-control" name="password">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号
		组成</small>
	</div>
		<a href="/register/" class="text-success">
			<ins>新用户注册</ins>
		</a>
		<button type="submit" class="btn btn-primary float-right">登录
</button>
	</form>
	
</div>

视图函数的完善

# login/views.py
def login(request):
	if request.method == 'POST':
		username = request.POST.get('username').strip()
		password = request.POST.get('password').strip()
	    # print(username, password)
		if username and password:
			user = SiteUser.objects.filter(name=username,password=password).first()
			if user:
				return redirect('/index/')
			else:
				message = "用户名或者密码错误"
				return render(request, 'login/login.html',
{
    
    'message':message})
		else:
			message = "非法的数据信息"
			return render(request, 'login/login.html', {
    
    'message': message})
	return render(request, 'login/login.html')

测试:

访问网址: http://127.0.0.1:9999/login/ 
填写正确的用户名和密码/错误的用户名和密码测试是否为期待的效果

在这里插入图片描述
输入正确密码则可以登入并看到测试页面:
在这里插入图片描述

6.session会话与登录的视图函数

登录成功, 存储登录的用户信息到session中

# login/views.py
def login(request):
	if request.method == 'POST':
		username = request.POST.get('username').strip()
		password = request.POST.get('password').strip()
		# print(username, password)
		if username and password:
		   user = SiteUser.objects.filter(name=username,password=password).first()
			if user:
				# ------------核心修改的内容开始
				request.session['is_login'] = True
				request.session['user_id'] = user.id
				request.session['username'] = user.name
				# --------------核心修改的内容结束
				return redirect('/index/')
			else:
				message = "用户名或者密码错误"
				return render(request, 'login/login.html',{
    
    'message':message})
	else:
		message = "非法的数据信息"
		return render(request, 'login/login.html', {
    
    'message': message})
	return render(request, 'login/login.html')

登出时,清空session信息

# login/views.py
def logout(request):
	# 如果状态不是登录状态,则无法登出
	if request.session.get('is_login'):
		request.session.flush()   %清空session信息
	return redirect('/login/')

在首页添加登出的超链接并测试

# templates/login/index.html
# 核心代码如下:
	<h1>你好, {
    
    {
    
     request.session.username }}, 这是首页的模拟界面</h1>
	<a href="/logout/"><strong style="font-size: 20px">登出</strong></a>

测试:访问网址http://127.0.0.1:9999/index/
在这里插入图片描述

五、图片验证码

1.创建表单模型

# /login/forms.py(新建的文件)
from captcha.fields import CaptchaField
from django import forms
class LoginForm(forms.Form):
	username = forms.CharField(label='用户名', required=True,
								min_length=4, max_length=128)
	password = forms.CharField(label="密码", required=True,
								min_length=4, max_length=10)
	captcha = CaptchaField(label="验证码")

2.视图逻辑优化

# login/views.py
def login(request):
	# 请求方法为POST提交
	if request.method == 'POST':
		# 修改1: 实例化表单对象
		login_form = LoginForm(request.POST)
		# 修改2: 验证表单数据的合法性
		if login_form.is_valid():
		# 修改3:获取表单填写的数据,数据清洗
			username = login_form.cleaned_data.get('username')
			password = login_form.cleaned_data.get('password')
			user = SiteUser.objects.filter(name=username,
password=password).first()
			if user:
				request.session['is_login'] = True
				request.session['user_id'] = user.id
				request.session['username'] = user.name
			return redirect('/index/')
			else:
				message = "用户名或者密码错误"
				# 修改4: locals()以字典方式返回当前所有的变量
				# eg:{
    
    'message':'xxxx', 'login_form':'xxx'}
				return render(request, 'login/login.html', locals())
		else:
			message = "填写的登录信息不合法"
			return render(request, 'login/login.html', locals())
# 请求方法是GET请求
login_form = LoginForm()
return render(request, 'login/login.html', locals())

3.Template页面优化

# templates/login/login.html(部分修改)

<h3 style="text-align: center">用户登录</h3>
# 修改1: 不同的报错,提示不同的信息
{
    
    % if login_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
	<strong>登录失败!</strong> 验证码不正确
</div>
{
    
    % elif message %}
<div class="alert alert-warning" role="alert">
	<strong>登录失败!</strong> {
    
    {
    
     message }}
</div>
{
    
    % endif %}

<div class="form-group">
	# 修改2<label>{
    
    {
    
     login_form.username.label }}</label>
	<input type="text" class="form-control" name="username">
</div>
<div class="form-group">
	# 修改3<label>{
    
    {
    
     login_form.password.label }}</label>
	<input type="password" class="form-control" name="password">
	<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
</div>

# 修改4: 最重要的,添加验证码表单
<div class="form-group">
	<label>{
    
    {
    
     login_form.captcha.label }}</label>
	{
    
    {
    
     login_form.captcha }}
</div>

4.验证

访问网址http://127.0.0.1:9999/index/**
用户名和密码都正确但验证码填写错误的时候:
在这里插入图片描述

5.locals方法示例

在这里插入图片描述
以字典的形式将输入的变量与其对应的值以键值对的形式输出:
在这里插入图片描述

六、邮箱注册

发送邮件功能测试

配置邮件信息

# loginRegister/settings.py
# mail configure(添加信息如下)
EMAIL_HOST = 'smtp.163.com' # 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '[email protected]'         %你的邮箱地址
EMAIL_HOST_PASSWORD = 'NFPOJZLTRPJPANZE' %不是邮箱的登录密码,而是授权码(如何获
取授权码)
EMAIL_USE_SSL = False # 不开启ssl

获取授权码

在这里插入图片描述
在这里插入图片描述

交互式环境下进行验证

Terminal输入命令> python manage.py shell
In [1]: from django.core.mail import send_mail
In [2]: from loginRegister.settings import EMAIL_HOST_USER
In [3]: send_mail("测试邮件", "content", EMAIL_HOST_USER,
['[email protected]', '[email protected]'])  %可同时向多个邮箱发送邮件,查看邮箱中是否收到邮件

基本的注册功能实现

注册表单

# login/forms.py
class RegisterForm(forms.Form):
username = forms.CharField(label="用户名", required=True, max_length=128)
password1 = forms.CharField(label="密码", max_length=256, required=True)
password2 = forms.CharField(label="确认密码", max_length=256, required=True)
email = forms.EmailField(label="邮箱地址")
captcha = CaptchaField(label='验证码')

实现注册视图

# login/views.py
def register(request):
	# 如果用户已经登录,则不能注册跳转到首页。
	if request.session.get('is_login', None):
		return redirect('/index/')
		# 如果是POST请求
	if request.method == 'POST':
		print(request.POST)
		register_form = RegisterForm(request.POST)
		message = "请检查填写的内容!"
		# 先验证提交的数据是否通过
		if register_form.is_valid():
			# 清洗数据
			username = register_form.cleaned_data.get('username')
			password1 = register_form.cleaned_data.get('password1')
			password2 = register_form.cleaned_data.get('password2')
			email = register_form.cleaned_data.get('email')
			print(locals())
			# 接下来判断用户名和邮箱是否已经被注册
			same_name_user = SiteUser.objects.filter(name=username)
			print(same_name_user)
			if same_name_user:
				message = '用户名已经存在'
				return render(request, 'login/register.html', locals())
			same_email_user = SiteUser.objects.filter(email=email)
			if same_email_user:
				message = '该邮箱已经被注册了!'
				return render(request, 'login/register.html', locals())
			# 将注册的信息存储到数据库,跳转到登录界面
			new_user = SiteUser(name=username, password=password1, email=email)
			new_user.save()
			return redirect('/login/')
	# 如果是GET请求,返回用户注册的html页面。
	register_form = RegisterForm()
	return render(request, 'login/register.html', locals())

Template模板的更改

# templates/login/register.html

<h3 style="text-align: center">用户注册</h3>
{
    
    % if register_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!</strong> 验证码不正确
</div>
{
    
    % elif message %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!</strong> {
    
    {
    
     message }}
</div>
{
    
    % endif %}
<form action="/register/" method="post">
	{
    
    % csrf_token %}
	<div class="form-group">
		<label>{
    
    {
    
     register_form.username.label }}</label>
		<input type="text" class="form-control" name="username">
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.email.label }}</label>
		<input type="email" class="form-control" name="email">
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.password1.label }}</label>
		<input type="password" class="form-control" name="password1">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.
</small>
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.password2.label }}</label>
		<input type="password" class="form-control" name="password2">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.
</small>
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.captcha.label }}</label>
		{
    
    {
    
     register_form.captcha }}
	</div>
	<a href="/login/" class="text-success">
		<ins>用户登录</ins>
	</a>
	<button type="submit" class="btn btn-primary float-right">注册</button>
</form>

测试

在这里插入图片描述

注册添加密码加密功能

在 login/utils.py 中编写一个hash函数:

def hash_code(s, salt='mysite'):# 加点盐
	h = hashlib.sha256()
	s += salt
	h.update(s.encode()) # update方法只接收bytes类型
	return h.hexdigest()

在 login/views.py 中修改login和register视图

def register(request):
	# .....省略部分代码
	new_user = SiteUser(name=username, password=hash_code(password1),email=email)
	# .....省略部分代码
	
def login(request):
	# .....省略部分代码
	user = SiteUser.objects.filter(name=username,password=hash_code(password)).first()
    # .....省略部分代码

测试:
在这里插入图片描述

邮件注册确认

创建模型

# /login/models.py

class SiteUser(models.Model):
	# .......
	has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")
	
class ConfirmString(models.Model):
	code = models.CharField(max_length=256, verbose_name="确认码")
	user = models.OneToOneField('SiteUser', on_delete=models.CASCADE)
	create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
	
	def __str__(self):
	return self.user.name + ":" + self.code
	
	class Meta:
	ordering = ["-create_time"]
	verbose_name = "确认码"
	verbose_name_plural = "确认码"

数据库模型修改,需要生成迁移脚本和写入数据库:

python manage.py makemigrations
python manage.py migrate

修改一下admin.py文件,方便我们在后台修改和观察数据:

# login/admin.py
admin.site.register(ConfirmString)

修改视图

def register(request):
	# ................
	code = make_confirm_string(new_user)
	send_email(email, code)
	message = '请前往邮箱进行确认!'
	# ..................

make_confirm_string() 是创建确认码对象的方法,代码如下:
import datetime
def make_confirm_string(user):
	now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
	code = hash_code(user.name, now)
	models.ConfirmString.objects.create(code=code, user=user,)
	return code

send_email(email, code) 方法接收两个参数,分别是注册的邮箱和前面生成的哈希值,代码如下:
def send_email(email, code):
	print('send mail.........')
	subject = '注册确认邮件'
	text_content = '''感谢注册,这里是登录注册系统网站!\如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
	html_content = '''
	<p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>点击验证</a>,\
	这里是登录注册系统网站!</p>
	<p>请点击站点链接完成注册确认!</p>
	<p>此链接有效期为{
    
    }天!</p>
	'''.format('127.0.0.1:9999', code, settings.CONFIRM_DAYS)
	
	send_mail(subject, text_content,
			settings.EMAIL_HOST_USER, [email, ], html_message=html_content)

最后的有效期天数为设置在settings中的 CONFIRM_DAYS 。下面是邮件相关的settings配置:
# 注册有效期天数
CONFIRM_DAYS = 3  %可根据需要设定

测试:注册一个用户,判断是否能收到确认邮件。
在这里插入图片描述

处理邮件确认请求

在login子应用的 urls.py 中添加一条url:
path('confirm/', views.user_confirm,name='confirm'),

其次,在 login/views.py 中添加一个 user_confirm 视图。
获取确认码信息
数据库中是否有该确认码,如果没有, 返回说是无效的请求
数据库中是否有该确认码,如果有, 判断是否过期? 如果过期,删除用户信息,否则更新用户信息。
def user_confirm(request):
	code = request.GET.get('code', None)
	message = ''
	try:
		confirm = ConfirmString.objects.get(code=code)
	except:
		message = '无效的确认请求!'
		return render(request, 'login/confirm.html', locals())
		
	create_time = confirm.create_time
	now = datetime.now()
	print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS))
	if now > create_time + timedelta(settings.CONFIRM_DAYS):
		confirm.user.delete()
		message = '您的邮件已经过期!请重新注册!'
	else:
		confirm.user.has_confirmed = True
		confirm.user.save()
		confirm.delete()
		message = '感谢确认,请使用账户登录!'
	return render(request, 'login/confirm.html', locals())

需要一个 confirm.html 页面,我们将它创建在 /login/templates/login/ 下面:
页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化。
<h1 style="margin-left: 100px;">{
    
    {
    
     message }}</h1>
<script>
window.setTimeout("window.location='/login/'",2000);
</script>

修改登录规则

既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:

if not user.has_confirmed:
	message = '该用户还未经过邮件确认!'
	return render(request, 'login/login.html', locals())

测试效果:
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/nk298120/article/details/114522371