前面我们已经完成了项目大部分内容,现在还剩下重要的注册功能没有实现。
显而易见,我们的注册页面也需要一个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='验证码')
说明:
同样地,类似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/
,class为form-register
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,创建一个用户实例,然后保存到数据库内。
对于注册的逻辑,不同的生产环境有不同的要求,请跟进实际情况自行完善,这里只是一个基本的注册过程,不能生搬照抄。
让我们看一下注册的页面:
你可以尝试用不同的情况进行注册,然后观察错误信息的提示:
最后进行一次成功地注册,会自动跳转到登录页面。我们进入admin后台,查看一下用户列表:
等等!我们好像忘了什么!我们到现在都还一直在用明文的密码!
对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用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后台,查看用户的密码情况:
再使用该用户登录一下,大功告成!
可以看到密码长度根据你哈希算法的不同,已经变得很长了,所以前面model中设置password字段时,不要想当然的将max_length
设置为16这么小的数字。
完成
models里面User表中补充电话吗?
没报错,但是点击新用户注册后,出现页面只有直接登录和注册,然后点击注册才能到注册页面??
是不是表单Form没有渲染?
重定向问题怎么解决 一登录就直接反复横跳
浏览器提示重定向次数过多
再 检查一下代码吧
注册的时候显示 表格不存在, ProgrammingError at /register/ (1146, "Table 'mysite.login_user' doesn't exist")
注册之后登录显示密码错误的原因是:登录视图那里的密码没加密if user.password == hash_code(password):
博主好,我想问下 通过模型的save()方法是如何直接保存到数据库的喃 源码看的我有点晕
这个得从Django的ORM系统说起....看源码我也晕^-^
博主好,登录时候是哪个环节把user.password进行了hash加密呢?我们用明文登录的时候直接使用user.password获得了明文的密码和数据库中的密码进行了比对,但使用哈希了之后,同样用user.passwordr方式却获得了哈希后的值来和数据库的密文进行比对,不太理解在登录时点提交按钮是在哪一步把我们在密码框里输入的原始密码变成哈希值的
在视图中对密码进行加密和对比
博主,same_name_user = models.User.objects.filter(name=username)这段代码会报错,报错信息如下User matching query does not exist. 是否需要使用try..except进行一场处理?
filter没查到不报错,get才会报错
register.html页面中间一堆标签能否向login.html那样一句{{ login_form }}来处理,需要在views.py怎么改?和login一样的思路吗
可以的。和登录视图一个思路。
注册成功后,在登录界面总是显示密码不正确
是不是登录那里的密码没加密if user.password == hash_code(password):
我的点击注册报错:InternalError at /register/ (1054, "Unknown column 'mysite_user.name' in 'field list'") 找过很多可能,但是还是这个错误
提示:未知的name字段。所以需要检查一下你在模型、视图和你的模板中关于name的引用。
找了还是没找到,我的代码和博客上的是一样的
您的login的url始终没有写路由
有写,在第4节
register函数中用户RegisterForm是一个类吧
就是你在forms中写的那个类
代码都一样,但是为什么密码没有输入框啊
老师,验证码的错误信息出不来了,不知道咋回施
注册总算搞定了
搞定了 开心吧 我现在很开心。。
验证码相关的语句就那么几句,应该如何查找当验证码输入错误时,页面直接跳转到新的注册页面,不提示验证码错误?
在不同浏览器会同时登陆同个账户,如何能做到同个账户只能登陆一次,第二次直接顶掉原来的账户??
保存所有用户的登陆状态。如果有一个来自不同ip的相同用户名登录,就将前面的ip注销掉。当然,也有别的办法。
这边注册之后的用户在/admin里面没办法登录啊
只有使用或继承了Django内置的auth.User模型生成的用户,并且is_staff=True,才能登录admin后台管理。
完成
解决方法:清空数据库user表的所有数据,重启服务就好了。此问题困扰了我许久...
怎么穷空数据库USER表的数据啊
注册失败,代码和你的一模一样,报的是Exception Type: IntegrityError Exception Value: UNIQUE constraint failed: login_user.email 这个怎么解决呀
再试试
不行,一直都是报我贴的那个错误,都检查好多次了,不知道问题是哪里
或者去github上下我的代码,然后运行。
博主,可以给下GitHub链接吗
报错了 怎么解决啊 UNIQUE constraint failed: login_user.email
我也遇到相同的问题了,请问解决了吗
new_user = models.User.objects.create(name=username, password=hash_code(password),sex=sex,email=email)
同样的报错
这个是唯一约束失败,就是说现在提交的email数据库里已经存在了,因为数据库的email字段你写了属性unique=True。测试的时候注意每次注册新用户把email随便改一改