《Django REST framework 入门到精通》视频课程上线了,全网稀缺资源,深度源码剖析,进阶高手之路!点击链接查看详情

10. 注册视图

阅读: 21433     评论:30

前面我们已经完成了项目大部分内容,现在还剩下重要的注册功能没有实现。

一、创建forms

显而易见,我们的注册页面也需要一个form表单。同样地,在/login/forms.py中添加一个新的表单类:

class RegisterForm(forms.Form):
    gender = (
        ('male', "男"),
        ('female', "女"),
    )
    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="确认密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    sex = forms.ChoiceField(label='性别', choices=gender)
    captcha = CaptchaField(label='验证码')

说明:

  • gender字典和User模型中的一样,其实可以拉出来作为常量共用,为了直观,特意重写一遍;
  • password1和password2,用于输入两遍密码,并进行比较,防止误输密码;
  • email是一个邮箱输入框;
  • sex是一个select下拉框;
  • 没有添加更多的input属性

二、完善register.html

同样地,类似login.html文件,我们手工在register.html中编写forms相关条目:

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <!-- Bootstrap CSS -->
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="{% static 'login/css/register.css' %}" rel="stylesheet"/>
    <title>注册</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
                <form class="form-register" action="/register/" method="post">

                    {% if register_form.captcha.errors %}
                        <div class="alert alert-warning">{{ register_form.captcha.errors }}</div>
                    {% elif message %}
                        <div class="alert alert-warning">{{ message }}</div>
                    {% endif %}

                  {% csrf_token %}
                  <h3 class="text-center">欢迎注册</h3>

                  <div class="form-group">
                      {{ register_form.username.label_tag }}
                      {{ register_form.username}}
                  </div>
                  <div class="form-group">
                      {{ register_form.password1.label_tag }}
                      {{ register_form.password1 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.password2.label_tag }}
                      {{ register_form.password2 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.email.label_tag }}
                      {{ register_form.email }}
                  </div>
                  <div class="form-group">
                      {{ register_form.sex.label_tag }}
                      {{ register_form.sex }}
                  </div>
                  <div class="form-group">
                      {{ register_form.captcha.label_tag }}
                      {{ register_form.captcha }}
                  </div>

                  <div>
                      <a href="/login/"  ><ins>直接登录</ins></a>
                      <button type="submit" class="btn btn-primary float-right">注册</button>
                  </div>
                </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用顺序是固定的#}
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>

  </body>
</html>

需要注意的是:

  • 编写了一个register.css样式文件
  • form标签的action地址为/register/,class为form-register
  • from中传递过来的表单变量名字为register_form
  • 最下面的链接修改为直接登录的链接

register.css样式文件的代码很简单,如下所示:

body {
  height: 100%;
  background-image: url('../image/bg.jpg');
}
.form-register {
  width: 100%;
  max-width: 400px;
  padding: 15px;
  margin: 0 auto;
}
.form-group {
  margin-bottom: 5px;
}
form a{
  display: inline-block;
  margin-top:25px;
  line-height: 10px;
}

三、实现注册视图

进入/login/views.py文件,现在来完善我们的register()视图:

def register(request):
    if request.session.get('is_login', None):
        return redirect('/index/')

    if request.method == 'POST':
        register_form = forms.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')
            sex = register_form.cleaned_data.get('sex')

            if password1 != password2:
                message = '两次输入的密码不同!'
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用户名已经存在'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱已经被注册了!'
                    return render(request, 'login/register.html', locals())

                new_user = models.User()
                new_user.name = username
                new_user.password = password1
                new_user.email = email
                new_user.sex = sex
                new_user.save()

                return redirect('/login/')
        else:
            return render(request, 'login/register.html', locals())
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())

从大体逻辑上,也是先实例化一个RegisterForm的对象,然后使用is_valide()验证数据,再从cleaned_data中获取数据。

重点在于注册逻辑,首先两次输入的密码必须相同,其次不能存在相同用户名和邮箱,最后如果条件都满足,利用ORM的API,创建一个用户实例,然后保存到数据库内。

对于注册的逻辑,不同的生产环境有不同的要求,请跟进实际情况自行完善,这里只是一个基本的注册过程,不能生搬照抄。

让我们看一下注册的页面:

image

你可以尝试用不同的情况进行注册,然后观察错误信息的提示:

image

最后进行一次成功地注册,会自动跳转到登录页面。我们进入admin后台,查看一下用户列表:

image

image

四、密码加密

等等!我们好像忘了什么!我们到现在都还一直在用明文的密码!

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

首先在login/views.py中编写一个hash函数:

import hashlib

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

使用了sha256算法,加了点盐。具体的内容可以参考站点内的Python教程中hashlib库章节。

然后,我们还要对login()和register()视图进行一下修改:

from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from . import forms
import hashlib
# Create your views here.


def hash_code(s, salt='mysite'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())
    return h.hexdigest()


def index(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')
    return render(request, 'login/index.html')


def login(request):
    if request.session.get('is_login', None):  # 不允许重复登录
        return redirect('/index/')
    if request.method == 'POST':
        login_form = forms.UserForm(request.POST)
        message = '请检查填写的内容!'
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')

            try:
                user = models.User.objects.get(name=username)
            except :
                message = '用户不存在!'
                return render(request, 'login/login.html', locals())

            if user.password == hash_code(password):
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['user_name'] = user.name
                return redirect('/index/')
            else:
                message = '密码不正确!'
                return render(request, 'login/login.html', locals())
        else:
            return render(request, 'login/login.html', locals())

    login_form = forms.UserForm()
    return render(request, 'login/login.html', locals())


def register(request):
    if request.session.get('is_login', None):
        return redirect('/index/')

    if request.method == 'POST':
        register_form = forms.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')
            sex = register_form.cleaned_data.get('sex')

            if password1 != password2:
                message = '两次输入的密码不同!'
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用户名已经存在'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱已经被注册了!'
                    return render(request, 'login/register.html', locals())

                new_user = models.User()
                new_user.name = username
                new_user.password = hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                new_user.save()

                return redirect('/login/')
        else:
            return render(request, 'login/register.html', locals())
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')

    request.session.flush()
    # del request.session['is_login']
    return redirect("/login/")

注意其中关于密码处理的部分!

好了,我们可以来验证一下了!但是,请先在admin后台里,把我们前面创建的测试用户全部删除!因为它们的密码没有使用哈希算法加密,已经无效了。

重启服务器,进入注册页面,新建一个用户,然后进入admin后台,查看用户的密码情况:

image

再使用该用户登录一下,大功告成!

可以看到密码长度根据你哈希算法的不同,已经变得很长了,所以前面model中设置password字段时,不要想当然的将max_length设置为16这么小的数字。


 9. session会话 11.Django发送邮件 

评论总数: 30


点击登录后方可评论

user_image

register.html页面中间一堆标签能否向login.html那样一句{{ login_form }}来处理,需要在views.py怎么改?和login一样的思路吗



user_image

可以的。和登录视图一个思路。



user_image

注册成功后,在登录界面总是显示密码不正确



user_image

我的点击注册报错:InternalError at /register/ (1054, "Unknown column 'mysite_user.name' in 'field list'") 找过很多可能,但是还是这个错误



user_image

提示:未知的name字段。所以需要检查一下你在模型、视图和你的模板中关于name的引用。



user_image

找了还是没找到,我的代码和博客上的是一样的



user_image

您的login的url始终没有写路由



user_image

有写,在第4节



user_image

register函数中用户RegisterForm是一个类吧



user_image

就是你在forms中写的那个类



user_image

代码都一样,但是为什么密码没有输入框啊



user_image

老师,验证码的错误信息出不来了,不知道咋回施



user_image

注册总算搞定了



user_image

搞定了 开心吧 我现在很开心。。



user_image

验证码相关的语句就那么几句,应该如何查找当验证码输入错误时,页面直接跳转到新的注册页面,不提示验证码错误?



user_image

在不同浏览器会同时登陆同个账户,如何能做到同个账户只能登陆一次,第二次直接顶掉原来的账户??



user_image

保存所有用户的登陆状态。如果有一个来自不同ip的相同用户名登录,就将前面的ip注销掉。当然,也有别的办法。



user_image

这边注册之后的用户在/admin里面没办法登录啊



user_image

只有使用或继承了Django内置的auth.User模型生成的用户,并且is_staff=True,才能登录admin后台管理。



user_image

完成



user_image

解决方法:清空数据库user表的所有数据,重启服务就好了。此问题困扰了我许久...



user_image

怎么穷空数据库USER表的数据啊



user_image

注册失败,代码和你的一模一样,报的是Exception Type: IntegrityError Exception Value: UNIQUE constraint failed: login_user.email 这个怎么解决呀



user_image

再试试



user_image

不行,一直都是报我贴的那个错误,都检查好多次了,不知道问题是哪里



user_image

或者去github上下我的代码,然后运行。



user_image

报错了 怎么解决啊 UNIQUE constraint failed: login_user.email



user_image

我也遇到相同的问题了,请问解决了吗



user_image

new_user = models.User.objects.create(name=username, password=hash_code(password),sex=sex,email=email)



user_image

同样的报错