33、发挥邮箱作用
1、邮箱发挥作用
- 减少垃圾用户
- 保证账户安全
- 推送消息(通知)
2、引导用户填写邮箱
可从注册的时候要求填写邮箱:
(1)发送邮件,填写验证码
(2)发送验证邮箱链接
(3)直接使用邮箱注册
当然也可以不要求填邮箱
建议:一经绑定,不可解绑
第一种方式:发送邮件,填写验证码
我们想在注册表单上加多一个验证码字段,点击“发送验证码”,填写上验证码就可以。
修改user/forms.py中的RegForm如下(复制BindEmailForm里面验证码那段代码):
另外,我们还要让页面有“发送验证码”的按钮,这里是注册页面,所以我们在user/templates/user/register.html中修改如下(从bind_email.html复制过来):
刷新注册表单的页面
我们提交注册表单,是在处理方法views.py中的register完成的,加进来验证码对其中的验证数据部分没有产生任何影响,所以我们只需要在form里面进行校验的时候对这个验证码进行处理,
所以我们回到forms.py,处理验证码
处理验证码在BindEmailForm里面我们也做过一定的处理,我们可以拿过来做一下处理,复制 判断验证码是否为空这块def clean_verification_code
到RegForm里面。
另外在BindEmailForm里面,在def __init__(self, *args, **kwargs):
这里还传了一个request参数,传这个参数主要是为了获取session,获取到session后就可以获取到发送的验证码是什么,我们发现获取的session里面的key指定是‘bind_email_code’,而我们刚刚发送验证码的地方(views.py中send_verification_code里)这里要保存session,保存验证码到key为‘bind_email_code’的session中,已经写固定了,这样不太好,因为在这个地方是注册发送的验证码而不是绑定邮箱发送的验证码,所以我们要把这个区分对待。
那区分对待的话,我们调用这个处理方法的时候,这里又不能传参数过来,那我们可以考虑参数从前端页面通过GET请求传进来,修改views.py如下:
添加了send_for之后,我们就有几个地方需要更改了,
绑定邮箱 发送验证码请求这里,data需要加一个‘send_for’字段,修改bind_email.html如下:
注册的地方,同样的发送验证码这里,data也需要加上‘send_for’字段,修改register.html如下:
对应的,有些地方就需要更改了,
把forms.py里的BindEmailForm里 判断验证码 和传入request 这块的代码,复制到RegForm里
那RegForm需要传入request这个参数的话,views.py中register这里,提交请求这里我们就需要把request参数给它传进来
这样,注册加了验证码 这块,我们就把逻辑实现出来了
测试一下,刷新页面,我们找一个新的邮箱进行注册,可以看到注册验证邮箱这一步已经完成
不过这里有一个bug没有处理。
在判断验证码之后,如果成功,session还是保留的,那有时候可能session会重复去使用,这样就可能会有问题,例如反复用同一个验证码在同一个电脑上反复去注册,这是可以的。因此这里就需要改下,需要清session。修改views.py如下:(在 forms.py 中的 RegForm 和 BindEmailForm 两处的 session,分别为‘bind_email_code’
和‘register_code’
,对应的views.py中的register
和bind_email
方法中,需要清除request.session
)
到这里,大家可能有一个疑问,既然我们注册的时候已经绑定邮箱了,我们还需要进行填写邮箱绑定邮箱操作吗?
这里有一个逻辑,我们一开始用用户名里面有个superuser超级管理员这个用户,我们用代码创建的时候没有写邮箱,所以是没有邮箱的,自然而然就产生了需要填写邮箱这个需求。所以如果不想做单独填写邮箱绑定邮箱的操作,我们可以把没有邮箱的superuser账号的邮箱补上去。
3、修改登录方式
用户名和邮箱均可以登录
修改forms.py 中的LoginForm:
刷新页面,我们尝试用用户名登录和用密码登录这两种方式,都能登陆成功
4、修改密码
- 1、登录的情况,修改密码
- 2、未登录情况,忘记密码
- 发送验证码到邮箱
- 修复bug(session未删除)
1、登录的情况,修改密码
对应的表单,需要增加一个修改密码的表单,修改forms.py如下:
# 修改密码的表单
class ChangePasswordForm(forms.Form):
# 三个字段:旧密码、新密码、再输一遍密码
old_password = forms.CharField(
label='旧的密码',
widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder': '请输入旧的密码'})
)
new_password = forms.CharField(
label='新的密码',
widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder': '请输入新的密码'})
)
new_password_again = forms.CharField(
label='请再次输入新的密码',
widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder': '请再次输入新的密码'})
)
def __init__(self, *args, **kwargs):
if 'user' in kwargs:
self.user = kwargs.pop('user')
super(ChangePasswordForm, self).__init__(*args, **kwargs)
# 验证新的密码是否一致
def clean(self):
new_password = self.cleaned_data.get('new_password', '')
new_password_again = self.cleaned_data.get('new_password_again', '')
if new_password != new_password_again or new_password == '':
raise forms.ValidationError('两次输入的密码不一致')
return self.cleaned_data
# 验证旧的密码是正确的
def clean_old_password(self):
old_password = self.cleaned_data.get('old_password', '')
if not self.user.check_password(old_password):
raise forms.ValidationError('旧的密码错误')
return old_password
验证旧的密码有两种方法:1、通过user对象的check_password()
方法去验证,这里传一个密码进来就可以;2、通过登陆的auth.authenticate,需要username这个对象,实际上是通过验证用户名跟密码是否是一致的,如果一致就返回一个非None的对象,如果密码不正确的话,就返回None。
对应的链接,修改urls.py如下:
对应的处理方法,修改views.py如下:
其中,密码修改成功之后,应该有其他的处理逻辑,例如修改成功之后登出,让用户再重新登录一遍
def change_password(request):
redirect_to = reverse('home')
if request.method == 'POST':
form = ChangePasswordForm(request.POST, user=request.user) # 获取form表单
if form.is_valid():
user = request.user
old_password = form.cleaned_data['old_password']
new_password = form.cleaned_data['new_password']
user.set_password(new_password)
user.save()
auth.logout(request)
return redirect(redirect_to)
else:
form = ChangePasswordForm()
context = {}
context['page_title'] = '修改密码'
context['form_title'] = '修改密码'
context['submit_text'] = '修改'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'form.html', context)
对应模板页面调用这个方法,修改user_info.html如下:
然后在导航栏右侧的下拉菜单那里,加上修改密码这个选项,修改base.html:
刷新页面
2、未登录情况,忘记密码
另外,还有个逻辑要处理,我们可能忘记密码,我们需要在表单上加上“忘记密码”对应的选项,找回密码需要怎么操作呢?最直接的方法是我们可以把密码发送到邮箱,但是django没有记录明文的密码,所以忘记密码最好的处理方法是重置密码,重置密码的话就需要验证这个账户是不是本人操作,最好是验证一下邮箱。
增加一个忘记密码的form表单,修改forms.py如下:
# 忘记密码的表单
class ForgotPasswordForm(forms.Form):
# 三个字段:邮箱、验证码、新的密码
email = forms.EmailField(
label='邮箱',
widget=forms.EmailInput(attrs={'class':'form-control', 'placeholder': '请输入绑定过的邮箱'})
)
verification_code = forms.CharField(
label='验证码',
required=False,
max_length=20,
widget=forms.TextInput(
attrs={'class':'form-control', 'placeholder': '点击“发送验证码”发送到邮箱'}
)
)
new_password = forms.CharField(
label='新的密码',
widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder': '请输入新的密码'})
)
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(ForgotPasswordForm, self).__init__(*args, **kwargs)
# 判断邮箱是否存在
def clean_email(self):
email = self.cleaned_data['email'].strip()
if not User.objects.filter(email=email).exists():
raise forms.ValidationError('邮箱不存在')
return email
# 判断验证码是否为空
def clean_verification_code(self):
verification_code = self.cleaned_data.get('verification_code', '').strip()
if verification_code == '':
raise forms.ValidationError('验证码不能为空')
# 判断验证码是否正确
code = self.request.session.get('forgot_password_code', '')
verification_code = self.cleaned_data.get('verification_code', '')
if not (code != '' and code == verification_code):
raise forms.ValidationError('验证码不正确')
return self.cleaned_data
修改urls.py:
修改views.py:
def forgot_password(request):
redirect_to = reverse('login')
if request.method == 'POST':
form = ForgotPasswordForm(request.POST, request=request) # 获取form表单
if form.is_valid(): # 验证通过,重置密码
email = form.cleaned_data['email']
new_password = form.cleaned_data['new_password']
user = User.objects.get(email=email)
user.set_password(new_password)
user.save()
# 清除session
del request.session['forgot_password_code']
return redirect(redirect_to)
else:
form = ForgotPasswordForm()
context = {}
context['page_title'] = '重置密码'
context['form_title'] = '重置密码'
context['submit_text'] = '重置'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'user/forgot_password.html', context)
重置密码的页面,新增user/templates/user/forgot_password.html(复制bind_email.html,稍作修改):
处理完之后,我们还缺一个入口,通常是通过登陆这个页面进去。修改login.html如下:
刷新页面,测试