ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python进阶6:Django5-表单(form)/图片验证码/邮箱注册

2021-03-19 14:57:58  阅读:150  来源: 互联网

标签:进阶 form Python register request 表单 user login


文章目录

1. 配置django-simple-captcha库

在网站开发的登录页面中,经常会需要使用到图形验证码来验证。在Django中,django-simple-captcha库包提供了图形验证码的使用。

安装
pip install django-simple-captcha

在这里插入图片描述
2.在settings.py文件中注册图形验证码

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
    'captcha'

]

3.迁移至数据库中

python manage.py migrate

在这里插入图片描述
4.在子路由文件urls.py中添加路径:

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'),
    path('confirm/', views.user_confirm,name='confirm'),
    path('captcha/',include('captcha.urls')),
]

2. 表单(form)-在Django中使用表单

我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。
即用html编写是面向过程的编写,用Django提供的表单是面向对象编写

Django的表单给我们提供了下面三个主要功能:

  • 准备和重构数据用于页面渲染;
  • 为数据创建HTML表单元素;
  • 接收和处理用户从表单发送过来的数据。

编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表<form>中的一个<input>元素。

2.1 创建表单模型

创建一个单独的文件用来定义表单:forms.py

# /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.2 视图逻辑优化

修改视图函数文件views.py,在此需要实例化一个表单,再将该表单传给前端:
在此编辑login部分:

# 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())

关于locals()函数用法:以字典的形式返回所有变量及值
在这里插入图片描述

2.3 Template页面优化

修改login.html:

# 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>

验证是否正确

在这里插入图片描述

3. 邮箱注册

3.1 配置邮件信息

3.1.1 配置文件

# loginRegister/settings.py

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

3.1.2 获取授权码

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

qq邮箱:
在这里插入图片描述

3.1.3 交互式环境中测试发送邮件是否成功?

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, ['ahalf_summer@163.com', 'ahalf_summer@126
   ...: .com'])

验证是否成功, 查看是否受到邮件

3.2 基本的注册功能实现

注册表单

# 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='验证码')

实现注册视图

  • 如果用户已经登录,则不能注册跳转到首页。
  • 如果是GET请求,返回用 户注册的html页面。
  • 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是否已经被注册, 将注册的信息存储到数据库,跳转到登录界面。
  • 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。
# 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>

测试是否成功
在这里插入图片描述

3.3 注册添加密码加密功能

对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用。

  1. login/utils.py中编写一个hash函数:
def hash_code(s, salt='mysite'):# 加点盐
	h = hashlib.sha256()
    s += salt    
    h.update(s.encode())  # update方法只接收bytes类型    
    return h.hexdigest()
  1. 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()
    # .....省略部分代码

3.4 邮箱注册

很自然地,我们会想到如果能用邮件确认的方式对新注册用户进行审查,既安全又正式,也是目前很多站点的做法。

3.4.1 创建模型

  • 既然要区分通过和未通过邮件确认的用户,那么必须给用户添加一个是否进行过邮件确认的属性。

  • 另外,我们要创建一张新表,用于保存用户的确认码以及注册提交的时间。

# /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)

3.4.2 修改视图

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

测试:注册一个用户,判断是否能收到确认邮件

3.4.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.is_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>

3.4.4 修改登录规则

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

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

标签:进阶,form,Python,register,request,表单,user,login
来源: https://blog.csdn.net/Fancyll_Lee/article/details/114996608

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有