完成登录页面
构建前端页面
其中比较主要的是前端的一个 form 类型的注册框:
<div class="reg_form clearfix">
<form method="post" action="/user/register/">
{% csrf_token %}
<ul>
<li>
<label>用户名:</label>
<input type="text" name="user_name" id="user_name">
<span class="error_tip">提示信息</span>
</li>
<li>
<label>密码:</label>
<input type="password" name="pwd" id="pwd">
<span class="error_tip">提示信息</span>
</li>
<li>
<label>确认密码:</label>
<input type="password" name="cpwd" id="cpwd">
<span class="error_tip">提示信息</span>
</li>
<li>
<label>邮箱:</label>
<input type="text" name="email" id="email">
<span class="error_tip">提示信息</span>
</li>
<li class="agreement">
<input type="checkbox" name="allow" id="allow" checked="checked">
<label>同意”天天生鲜用户使用协议“</label>
<span class="error_tip2">提示信息</span>
</li>
<li class="reg_sub">
<input type="submit" value="注 册" name="">
</li>
</ul>
</form>
{{ errmsg }}
</div>
注册路由
在 apps.user.urls 中注册路由:
from django.conf.urls import url
from apps.user.views import RegisterView
urlpatterns = [
url(r"^register/$", RegisterView.as_view(), name='register'), # 将注册改为类视图的使用模式
]
编写视图
首先完成页面视图,即我们请求注册页面时发出 get 请求的视图: 在 apps.user.views.py 中使用类视图完成:
from django.shortcuts import render
from django.views import View
class RegisterView(View):
"""类视图"""
def get(self, request):
return render(request, "register.html")
这一步完成之后,我们请求 http://127.0.0.1:8000/user/register/ 即可访问到注册页。
接着我们来完成用户提交操作对应的视图:
import re
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from apps.user.models import User
class RegisterView(View):
"""类视图"""
def get(self, request):
return render(request, "register.html")
def post(self, request):
return register_handle(request)
def register_handle(request):
user_name = request.POST.get("user_name")
password = request.POST.get("pwd")
email = request.POST.get("email")
if not all([user_name, password, email]):
return render(request, "register.html", {"errmsg": '数据不完整'})
try:
is_exist = User.objects.get(username=user_name)
except User.DoesNotExist: # 在查询不到的时候抛出异常
is_exist = None
if is_exist:
return render(request, "register.html", {"errmsg": '该用户名已存在'})
if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return render(request, "register.html", {"errmsg": '邮箱格式不正确'})
if not request.POST.get("allow") == "on":
return render(request, "register.html", {"errmsg": '请先同意用户协议'})
user = User.objects.create_user(user_name, email, password)
user.is_active = 0
user.save()
# TODO 发送激活邮件
return HttpResponse("注册成功 请到邮箱激活登录")
在页面完成注册,成功响应后查看数据库,可以看到数据库中已经出现了这个注册的用户。
只不过用户的状态是未激活: is_active = 0 ,我们通过下一小节中的发送邮件 --> 用户点击激活链接 --> 校验后更该用户状态来激活我们的注册用户。
发送邮件
在 django 中发送邮件,首先我们需要去邮箱服务中申请自己的一个服务授权密码。 这部分的内容可以参考以前的文章: blog.csdn.net/Enjolras_fu…
在这里不再赘述。
首先我们来新增一些邮件相关的配置:
# 邮件相关的配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '15626046299@163.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = EMAIL_HOST_PASSWORD
# 收件人看到的发件人
EMAIL_FROM = 'mydailyfresh<15626046299@163.com>' # 注意尖括号内的邮箱必须和上面的邮箱地址一致 否则发送不出去
其中, EMAIL_HOST_PASSWORD 说我的客户端服务授权码,在这里应该将其换成自己的授权码,同时更改为自己的邮箱地址。
接着,我们在 apps.user.views 中林临时创建一个函数来封装发送邮件的过程:
def my_send_mail(msg, user_email):
'''
msg: 需要发送的信息
user_email: 用户填写的接收激活信息的邮箱
'''
send_mail("dailyfrsh 用户注册",
'',
settings.EMAIL_FROM,
[user_email],
html_message=msg
)
我们在终端模拟一次邮件的发送过程:
查看邮箱,可以看到刚刚测试发送的内容:
注册时发送激活邮件逻辑
安装 itsdangerous
pip install itsdangerous
这个库是用来生成激活 token 的。
我们来思考一下使用 itsdangerous 的逻辑:
用户注册请求请求发送成功之后,我们在数据库中生成一个未激活用户。然后我们将用户的 id 加密后发送给用户的注册邮箱,用户从自己的邮箱中点击注册链接,后端拿到链接参数解密后将相应 id 的用户激活。
需要注意的几个点:1 是加密和解密的秘钥是不能泄露的;2 是加密应该具有时效性。
我们来进行一次脚本测试:
现在我们就可以将这段逻辑加入 用户的注册视图中了 :
# ...
# TODO 发送激活邮件
ser = Serializer(settings.SECRET_KEY, 3600) # 参数分别是秘钥以及以秒为单位的过期时间
user_info = {"confirm": user.id}
ret = ser.dumps(user_info)
token = ret.decode("utf-8")
active_link = "http://127.0.0.1:8000/user/active?token={}".format(token)
my_send_mail(active_link, user.email)
# ...
位置在注册用户创建之后:
完成激活逻辑视图
最后我们还要增加最后一个视图,用来接收用户点击了激活链接后的逻辑处理。 这一步基本的功能就是解密、修改用户用户信息了。
# apps.user.views.py
class ActiveView(View):
"""用户激活视图"""
def get(self, request):
token = request.GET.get("token")
token = token.encode("utf-8")
ser = Serializer(settings.SECRET_KEY, 3600)
try:
user_info = ser.loads(token)
except SignatureExpired:
return HttpResponse("激活链接已超时")
except:
err_msg = traceback.format_exc()
return HttpResponse("激活失效: {}".format(err_msg))
else:
# 激活成功
user_id = user_info.get("confirm")
user = User.objects.get(id=user_id)
user.is_active = 1
user.save()
# 跳转到登录页面
return HttpResponse("登录页面")
# TODO 跳转到真正的登录页面
# return redirect(reverse("user:login"))
# apps.user.urls.py
urlpatterns = [
# ...
url(r"^active/$", ActiveView.as_view(), name='active'),
# ...
]
登录
django 内置了用户登录登出,以及状态校验的一些方法,使完成用户的登录登出操作十分方便。
照例我们首先来完成登录的前端页面: 给出主体部分的 form 的 html 代码:
<div class="form_input">
<!-- 不设置表单的 action 属性时,提交表单时 会向浏览器当前所在地址提交请求 -->
<form method="post">
{% csrf_token %}
<input type="text" name="username" class="name_input" value="{{ username }}" placeholder="请输入用户名">
<div class="user_error">输入错误</div>
<input type="password" name="pwd" class="pass_input" placeholder="请输入密码">
<div class="pwd_error">输入错误</div>
<div class="more_input clearfix">
<input type="checkbox" name="remember">
<label>记住用户名</label>
<a href="#">忘记密码</a>
</div>
<input type="submit" name="" value="登录" class="input_submit">
</form>
</div>
增加路由:
# apps.user.urls.py
urlpatterns = [
# ...
url(r"^login/$", LoginView.as_view(), name='login'), # 用户登录
url(r"^logout/$", LogoutView.as_view(), name='logout'), # 用户登出
]
对应添加两个视图 LoginView 和 LogoutView。 登录后的下一步逻辑是跳转到首页,我们暂时模拟替代。
class LoginView(View):
"""用户登录视图"""
def get(self, request):
if "username" in request.COOKIES:
user_name = request.COOKIES.get("username")
else:
user_name = ''
return render(request, "login.html", {"username": user_name})
def post(self, request):
user_name = request.POST.get("username")
pass_word = request.POST.get("pwd")
if not all([user_name, pass_word]):
return render(request, 'login.html', {"errmsg": "数据不完整"})
user = authenticate(username=user_name, password=pass_word)
if not user:
return render(request, 'login.html', {"errmsg": "用户不存在或者密码错误"})
elif not user.is_active:
return render(request, 'login.html', {"errmsg": "用户尚未激活"})
login(request, user)
response = HttpResponse("登录成功 ")
is_remembered = request.POST.get("remember")
if is_remembered:
response.set_cookie("username", user.username, max_age=7*24*3600)
else:
response.delete_cookie('username')
return response
class LogoutView(View):
"""用户登录视图"""
def get(self, request):
logout(request)
return HttpResponse("模拟首页")
这里需要注意的几个问题, response.set_cookie 、delete_cookie 以及 get_cookie 方法是与前端交互的,将 cookie 信息存储在浏览器中。当下一次浏览器发起请求的时候,可以从 request.COOKIES 中获取。