1、退出登录
注册界面跳转到登陆界面:templates/register.html
前端注册界面
{# 跳转到登陆界面 #}
<a href="{% url 'users:login' %}">登录</a>
logout()方法介绍
- 退出登录:
• 回顾登录:将通过认证的用户的唯一标识信息,写入到当前session会话中
• 退出登录:正好和登录相反(清理session会话信息) - logout()方法:
• Django用户认证系统提供了logout()方法
• 封装了清理session的操作,帮助我们快速实现登出一个用户 - logout()位置:
• django.contrib.auth.init.py文件中
logout(request)
logout()方法使用
class LogoutView(View):
"""退出登录"""
def get(self, request):
"""实现退出登录逻辑"""
# 清理session
logout(request)
# 退出登录,重定向到登录页
response = redirect(reverse('contents:index'))
# 退出登录时清除cookie中的username
response.delete_cookie('username')
return response
实现代码如下:
apps/users/views.py
文件,用户后端验证视图文件
"""
视图文件
apps/users/views.py文件,用户后端验证视图文件
"""
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse, JsonResponse
from django.views import View
from .forms import RegisterForm, LoginForm
from .models import User
from django.contrib.auth import login, logout, authenticate # authenticate封装的验证用户名和密码是否正确的方法
from django_redis import get_redis_connection
# 退出登录
class LogoutView(View):
"""退出登陆逻辑实现"""
def get(self, request):
"""实现用户退出登录的功能"""
# 清除状态保持信息
logout(request)
# 退出登录之后重定向到首页
response = redirect(reverse('contents:index'))
# 删除cookies中的用户名
# result.set_cookie('username', user.username, max_age=3600*24*14) # 保存两周
response.delete_cookie('username')
return response # 响应结果
# 用户登陆
class LoginView(View):
"""用户名登陆"""
def get(self, request):
""" 提供登陆界面
:return: 登陆界面
"""
return render(request, 'login.html')
def post(self, request):
"""
实现登录逻辑
:param request: 请求对象
:return: 登录结果
"""
# 接受参数
login_form = LoginForm(request.POST)
# 校验参数
if login_form.is_valid():
# 接收参数
username = login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
remembered = request.POST.get('remembered') # 没经过form验证,使用request接收参数
# 认证登录用户
# users = User.objects.get(username=username)
# users.check_password(password) # check_password验证密码封装的方法,返回值bool类型
""" authenticate方法源码
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
"""
user = authenticate(username=username, password=password) # 重构authenticate方法之后,可以验证手机号登录和用户名登录
if user is None:
# 用户名或者密码输入错误
return render(request, 'login.html', {"errmsg": "用户名或者密码输入错误"})
# 实现状态保持
login(request, user)
# 设置状态保持的周期
if remembered != 'on':
# 没选中记住密码,浏览器关闭就需要销毁session信息
request.session.set_expiry(0) # set_expiry过期时间
else:
# 选中记住密码,session信息默认保存两周
# request.session.set_expiry(60*60*24*14)
request.session.set_expiry(None)
# 后端将用户信息存入cookie
result = redirect(reverse('contents:index')) # redirect方法源码中会返回一个redirect_class
# set_cookie('key', 'value', 'erpriy') erpriy过期时间
result.set_cookie('username', user.username, max_age=3600*24*14) # 保存两周
# 响应登录结果 跳转到首页
return result
else:
print(login_form.errors.get_json_data())
context = {
"form_errors": login_form.errors,
}
return render(request, 'login.html', context=context)
class RegisterView(View):
"""用户注册"""
def get(self, request):
"""提供用户的注册界面"""
return render(request, 'register.html')
def post(self, request):
"""提供用户的注册逻辑"""
# 前端用户提交数据
form = RegisterForm(request.POST)
if form.is_valid():
# 接收参数
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
mobile = form.cleaned_data.get('mobile')
sms_code_client = request.POST.get('sms_code') # 验证短信验证码 sms_code是register.html 文件中命名的
# 判断用户输入的短信验证码是否正确
redis_conn = get_redis_connection('verify_code') # 链接redis中配置的数据库
sms_code_server = redis_conn.get('sms_%s' % mobile) # 根据存储时候的格式写
if sms_code_server is None:
return render(request, 'register.html', {'sms_code_errmsg': '短信验证码已经失效'}) # 错误信息渲染到前端界面
if sms_code_server.decode() != sms_code_client: # sms_code_server数据类型需要转换
return render(request, 'register.html', {'sms_code_errmsg': '短信验证码填写错误'})
try:
# user = User(username=username, password=password, mobile=mobile)
# 下面的添加数据的方法是封装了加密等功能的函数,更安全
users = User.objects.create_user(username=username, password=password, mobile=mobile)
except: # 如果保存数据失败
return render(request, 'register.html', {'register_error_message': '注册失败'})
# 保持用户登录的状态
login(request, users)
# 返回响应
# return HttpResponse('success')
return redirect(reverse('contents:index')) # 注册成功,跳转到首页
else:
print(form.errors.get_json_data())
# return HttpResponse("fail")
# 返回注册错误信息到前端界面
context = {
'form_error': form.errors,
}
return render(request, 'register.html', context=context)
# 判断用户名是否已经存在
class UsernameExists(View):
""" 判断用户名是否已经存在"""
def get(self, request, username): # username用户名
count = User.objects.filter(username=username).count() # 查询数据库中信息
return JsonResponse({"code": 0, "errmsg": "OK", "count": count}) # 返回给前端界面
前端界面绑定
首页界面:templates/index.html
{# 首页界面:templates/index.html #}
{% load static %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>LG商城-首页</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
<script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
</head>
<body>
<div id="app">
<div class="header_con" v-cloak> {# v-cloak加载显示的问题 #}
<div class="header">
<div class="welcome fl">欢迎来到LG商城!</div>
<div class="fr">
{# 方法一 登录用户的名称显示 #}
{# {% if user.is_authenticated %}#}
{# <div class="login_btn fl">#}
{# 欢迎您:<em>{{ user.username }}</em>#}
{# <span>|</span>#}
{# <a href="#">退出</a>#}
{# </div>#}
{# {% else %} {# 用户未登录,显示为 #}
{# <div class="login_btn fl">#}
{# <a href="login.html">登录</a>#}
{# <span>|</span>#}
{# <a href="register.html">注册</a>#}
{# </div>#}
{# {% endif %}#}
{# 方法三: Vue读取cookie渲染用户信息 v-if="username"有值就显示 #}
<div v-if="username" class="login_btn fl">
欢迎您:<em>[[ username ]]</em>
<span>|</span>
{# 绑定url,users是实例命名空间 #}
<a href="{% url 'users:logout' %}">退出</a>
</div>
{# v-else #}
<div v-else class="login_btn fl">
<a href="{% url 'users:login' %}">登录</a>
<span>|</span>
<a href="{% url 'users:register' %}">注册</a>
</div>
<div class="user_link fl">
<span>|</span>
<a href="user_center_info.html">用户中心</a>
<span>|</span>
<a href="cart.html">我的购物车</a>
<span>|</span>
<a href="user_center_order.html">我的订单</a>
</div>
</div>
</div>
</div>
<div class="search_bar clearfix">
<a href="index.html" class="logo fl"><img src="{% static 'images/1.png' %}"></a>
<div class="search_wrap fl">
<form method="get" action="/search/" class="search_con">
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
<ul class="search_suggest fl">
<li><a href="#">索尼微单</a></li>
<li><a href="#">优惠15元</a></li>
<li><a href="#">美妆个护</a></li>
<li><a href="#">买2免1</a></li>
</ul>
</div>
<div class="guest_cart fr">
<a href="cart.html" class="cart_name fl">我的购物车</a>
<div class="goods_count fl" id="show_count">2</div>
<ul class="cart_goods_show">
<li>
<img src="../static/images/goods/goods001.jpg" alt="商品图片">
<h4>华为 HUAWEI P10 Plus 6GB+64GB 钻雕金 移动联通电信4G手机 双卡双待</h4>
<div>1</div>
</li>
<li>
<img src="../static/images/goods/goods002.jpg" alt="商品图片">
<h4>Apple iPhoneX 64GB 深空灰色 移动联通电信4G手机</h4>
<div>1</div>
</li>
</ul>
</div>
</div>
<div class="navbar_con">
<div class="navbar">
<h1 class="fl">商品分类</h1>
<ul class="sub_menu">
<li>
<div class="level1">
<a href="http://shouji.jd.com/">手机</a><a href="#">运营商</a><a href="#">数码</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">手机通讯 ></div>
<div class="group_detail fl">
<a href="list.html">手机</a>
<a href="#">对讲机</a>
<a href="#">以旧换新</a>
<a href="#">手机维修</a>
</div>
</div>
<div class="list_group">
<div class="group_name fl">运营商 ></div>
<div class="group_detail fl">
<a href="#">合约机</a>
<a href="#">选号码</a>
<a href="#">固话宽带</a>
<a href="#">办套餐</a>
<a href="#">充话费/流量</a>
<a href="#">中国电信</a>
<a href="#">中国移动</a>
<a href="#">中国联通</a>
<a href="#">京东通信</a>
<a href="#">170选号</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">电脑</a><a href="#">办公</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">电脑 ></div>
<div class="group_detail fl">
<a href="#">曲面电视</a>
<a href="#">超薄电视</a>
<a href="#">HDR电视</a>
<a href="#">OLED电视</a>
<a href="#">4K超清电视</a>
<a href="#">人工智能电视</a>
<a href="#">55英寸</a>
<a href="#">65英寸</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">家居</a><a href="#">家具</a><a href="#">家装</a><a href="#">厨具</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">家具 ></div>
<div class="group_detail fl">
<a href="#">曲面电视</a>
<a href="#">超薄电视</a>
<a href="#">HDR电视</a>
<a href="#">OLED电视</a>
<a href="#">4K超清电视</a>
<a href="#">人工智能电视</a>
<a href="#">55英寸</a>
<a href="#">65英寸</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">男装</a><a href="#">女装</a><a href="#">童装</a><a href="#">内衣</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">男装 ></div>
<div class="group_detail fl">
<a href="#">男装</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">女鞋</a><a href="#">箱包</a><a href="#">钟表</a><a href="#">珠宝</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">女鞋 ></div>
<div class="group_detail fl">
<a href="#">女鞋</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">男鞋</a><a href="#">运动</a><a href="#">户外</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">男鞋 ></div>
<div class="group_detail fl">
<a href="#">男鞋</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">房产</a><a href="#">汽车</a><a href="#">汽车用品</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">汽车用品 ></div>
<div class="group_detail fl">
<a href="#">汽车用品</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">母婴</a><a href="#">玩具乐器</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">玩具乐器 ></div>
<div class="group_detail fl">
<a href="#">玩具乐器</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">食品</a><a href="#">酒类</a><a href="#">生鲜</a><a href="#">特产</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">食品 ></div>
<div class="group_detail fl">
<a href="#">食品</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">图书</a><a href="#">音像</a><a href="#">电子书</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">图书 ></div>
<div class="group_detail fl">
<a href="#">图书</a>
</div>
</div>
</div>
</li>
<li>
<div class="level1">
<a href="#">机票</a><a href="#">酒店</a><a href="#">旅游</a><a href="#">生活</a>
</div>
<div class="level2">
<div class="list_group">
<div class="group_name fl">机票 ></div>
<div class="group_detail fl">
<a href="#">机票</a>
</div>
</div>
</div>
</li>
</ul>
<ul class="navlist fl">
<li><a href="">首页</a></li>
<li class="interval">|</li>
<li><a href="">真划算</a></li>
<li class="interval">|</li>
<li><a href="">抽奖</a></li>
</ul>
</div>
</div>
<div class="pos_center_con clearfix">
<ul class="slide">
<li><a href="#"><img src="../static/images/slide01.jpg" alt="幻灯片01"></a></li>
<li><a href="#"><img src="../static/images/slide02.jpg" alt="幻灯片02"></a></li>
<li><a href="#"><img src="../static/images/slide03.jpg" alt="幻灯片03"></a></li>
<li><a href="#"><img src="../static/images/slide04.jpg" alt="幻灯片04"></a></li>
</ul>
<div class="prev"></div>
<div class="next"></div>
<ul class="points"></ul>
<div class="news">
<div class="news_title">
<h3>快讯</h3>
<a href="#">更多 ></a>
</div>
<ul class="news_list">
<li><a href="#">i7顽石低至4199元</a></li>
<li><a href="#">奥克斯专场 正1匹空调1313元抢</a></li>
<li><a href="#">荣耀9青春版 高配 领券立减220元</a></li>
<li><a href="#">LG探索公益新模式</a></li>
<li><a href="#">i7顽石低至4199元</a></li>
<li><a href="#">正1匹空调1313元抢</a></li>
<li><a href="#">奥克斯专场 正1匹空调1313元抢</a></li>
</ul>
<a href="#" class="advs"><img src="../static/images/adv01.jpg"></a>
</div>
</div>
<div class="floor_adv" v-cloak>
<div class="list_model">
<div class="list_title clearfix">
<h3 class="fl" id="model01">1F 手机通讯</h3>
<div class="subtitle fr">
<a @mouseenter="f1_tab=1" :class="f1_tab===1?'active':''">时尚新品</a>
<a @mouseenter="f1_tab=2" :class="f1_tab===2?'active':''">畅想低价</a>
<a @mouseenter="f1_tab=3" :class="f1_tab===3?'active':''">手机配件</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="../static/images/banner01.jpg">
<div class="channel">
<a href="#">手机</a>
<a href="#">配件</a>
<a href="#">充值</a>
<a href="#">优惠券</a>
</div>
<div class="key_words">
<a href="#">荣耀手机</a>
<a href="#">国美手机</a>
<a href="#">华为手机</a>
<a href="#">热销推荐</a>
<a href="#">以旧换新</a>
<a href="#">潮3C</a>
<a href="#">全面屏</a>
<a href="#">守护宝</a>
<a href="#">存储卡</a>
<a href="#">保护套</a>
</div>
</div>
<div class="goods_list_con">
<ul v-show="f1_tab===1" class="goods_list fl">
<li>
<a href="http://itcast.cn/" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#" title="360手机 N6 Pro 全网通 6GB+128GB 极夜黑">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
</ul>
<ul v-show="f1_tab===2" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#" title="360手机 N6 Pro 全网通 6GB+128GB 极夜黑">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
</ul>
<ul v-show="f1_tab===3" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#" title="360手机 N6 Pro 全网通 6GB+128GB 极夜黑">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
</ul>
</div>
</div>
</div>
<div class="list_model model02">
<div class="list_title clearfix">
<h3 class="fl" id="model01">2F 电脑数码</h3>
<div class="subtitle fr">
<a @mouseenter="f2_tab=1" :class="f2_tab===1?'active':''">加价换购</a>
<a @mouseenter="f2_tab=2" :class="f2_tab===2?'active':''">畅享低价</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="../static/images/banner02.jpg">
<div class="channel">
<a href="#">电脑</a>
<a href="#">数码</a>
<a href="#">配件</a>
<a href="#">潮电子</a>
</div>
<div class="key_words">
<a href="#">Apple</a>
<a href="#">神舟战神</a>
<a href="#">单反相机</a>
<a href="#">智能家居</a>
<a href="#">智能路由</a>
<a href="#">限时抢</a>
<a href="#">顽石</a>
<a href="#">微单</a>
<a href="#">耳机</a>
<a href="#">投影机</a>
</div>
</div>
<div class="goods_list_con">
<ul v-show="f2_tab===1" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods008.jpg"></a>
<h4><a href="#" title="华硕明星爆款 八代i7顽石低至4199">华硕明星爆款 八代i7顽石低至4199</a></h4>
<div class="price">¥ 4199.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
</ul>
<ul v-show="f2_tab===2" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods008.jpg"></a>
<h4><a href="#" title="华硕明星爆款 八代i7顽石低至4199">华硕明星爆款 八代i7顽石低至4199</a></h4>
<div class="price">¥ 4199.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
</ul>
</div>
</div>
</div>
<div class="list_model model03">
<div class="list_title clearfix">
<h3 class="fl" id="model01">3F 家居家装</h3>
<div class="subtitle fr">
<a @mouseenter="f3_tab=1" :class="f3_tab===1?'active':''">生活用品</a>
<a @mouseenter="f3_tab=2" :class="f3_tab===2?'active':''">厨房用品</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="../static/images/banner03.jpg">
<div class="channel">
<a href="#">家居日用</a>
<a href="#">家纺寝居</a>
<a href="#">住宅家具</a>
</div>
<div class="key_words">
<a href="#">厨具餐饮</a>
<a href="#">被子</a>
<a href="#">实木床</a>
<a href="#">箭牌马桶</a>
<a href="#">指纹锁</a>
<a href="#">电饭煲</a>
<a href="#">热水器</a>
<a href="#">席梦思</a>
<a href="#">沙发</a>
<a href="#">酒柜</a>
</div>
</div>
<div class="goods_list_con">
<ul v-show="f3_tab===1" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods009.jpg"></a>
<h4><a href="#" title="维达3层超韧120抽软抽纸每提4包【大规格L】V2240(4包)">维达3层超韧120抽软抽纸每提4包【大规格L】V2240(4包)</a></h4>
<div class="price">¥ 18.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
</ul>
<ul v-show="f3_tab===2" class="goods_list fl">
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods009.jpg"></a>
<h4><a href="#" title="维达3层超韧120抽软抽纸每提4包【大规格L】V2240(4包)">维达3层超韧120抽软抽纸每提4包【大规格L】V2240(4包)</a></h4>
<div class="price">¥ 18.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods003.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 2699.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods002.jpg"></a>
<h4><a href="#">iphoneX N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 7788.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods001.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 1988.00</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods005.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 4288.80</div>
</li>
<li>
<a href="#" class="goods_pic"><img src="../static/images/goods/goods004.jpg"></a>
<h4><a href="#">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4>
<div class="price">¥ 3688.00</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="footer">
<div class="foot_link">
<a href="#">关于我们</a>
<span>|</span>
<a href="#">联系我们</a>
<span>|</span>
<a href="#">招聘人才</a>
<span>|</span>
<a href="#">友情链接</a>
</div>
<p>CopyRight © 2016 北京LG商业股份有限公司 All Rights Reserved</p>
<p>电话:010-****888 京ICP备*******8号</p>
</div>
</div>
<script type="text/javascript" src="{% static 'js/common.js' %}"></script>
<script type="text/javascript" src="{% static 'js/slide.js' %}"></script>
<script type="text/javascript" src="{% static 'js/index.js' %}"></script>
</body>
</html>
2、判断用户是否登录
展示用户中心界面
个人用户中心前端界面文件:templates/user_center_info.html
{# 个人用户中心前端界面文件:templates/user_center_info.html #}
{% load static %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>LG商城-用户中心</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
<script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
<script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
</head>
<body>
<div id="app">
<div class="header_con">
<div class="header" v-cloak>
<div class="welcome fl">欢迎来到LG商城!</div>
<div class="fr">
<div v-if="username" class="login_btn fl">
欢迎您:<em>[[ username ]]</em>
<span>|</span>
<a href="{% url 'users:logout' %}">退出</a>
</div>
<div v-else class="login_btn fl">
<a href="{% url 'users:login' %}">登录</a>
<span>|</span>
<a href="{% url 'users:register' %}">注册</a>
</div>
<div class="user_link fl">
<span>|</span>
<a href="{% url 'users:info' %}">用户中心</a>
<span>|</span>
<a href="cart.html">我的购物车</a>
<span>|</span>
<a href="user_center_order.html">我的订单</a>
</div>
</div>
</div>
</div>
<div class="search_bar clearfix">
{# contents命名空间 #}
<a href="{% url 'contents:index' %}" class="logo fl"><img src="{% static 'images/1.png' %}"></a>
<div class="search_wrap fl">
<form method="get" action="/search/" class="search_con">
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
<ul class="search_suggest fl">
<li><a href="#">索尼微单</a></li>
<li><a href="#">优惠15元</a></li>
<li><a href="#">美妆个护</a></li>
<li><a href="#">买2免1</a></li>
</ul>
</div>
</div>
<div class="main_con clearfix">
<div class="left_menu_con clearfix">
<h3>用户中心</h3>
<ul>
<li><a href="{% url 'users:info' %}" class="active">· 个人信息</a></li>
<li><a href="">· 收货地址</a></li>
<li><a href="user_center_order.html">· 全部订单</a></li>
<li><a href="user_center_pass.html">· 修改密码</a></li>
</ul>
</div>
<div class="right_content clearfix" v-cloak>
<div class="info_con clearfix">
<h3 class="common_title2">基本信息</h3>
<ul class="user_info_list">
<li><span>用户名:</span>居然</li>
<li><span>联系方式:</span>186461111</li>
<li>
<span>Email:</span>
<div v-if="set_email">
<input v-model="email" @blur="check_email" type="email" name="email" class="email">
<input @click="save_email" type="button" name="" value="保 存">
<input @click="cancel_email" type="reset" name="" value="取 消">
<div v-show="error_email" class="error_email_tip">邮箱格式错误</div>
</div>
<div v-else>
<input v-model="email" type="email" name="email" class="email" readonly>
<div v-if="email_active">
已验证
</div>
<div v-else>
待验证<input @click="save_email" :disabled="send_email_btn_disabled" type="button" :value="send_email_tip">
</div>
</div>
</li>
</ul>
</div>
<h3 class="common_title2">最近浏览</h3>
<div class="has_view_list" v-cloak>
<ul class="goods_type_list clearfix">
<li v-for="sku in histories">
<a :href="sku.url"><img :src="sku.default_image_url"></a>
<h4><a :href="sku.url">[[ sku.name ]]</a></h4>
<div class="operate">
<span class="price">¥[[ sku.price ]]</span>
<span class="unit">台</span>
<a href="javascript:;" class="add_goods" title="加入购物车"></a>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="footer">
<div class="foot_link">
<a href="#">关于我们</a>
<span>|</span>
<a href="#">联系我们</a>
<span>|</span>
<a href="#">招聘人才</a>
<span>|</span>
<a href="#">友情链接</a>
</div>
<p>CopyRight © 2016 北京LG商业股份有限公司 All Rights Reserved</p>
<p>电话:010-****888 京ICP备*******8号</p>
</div>
</div>
<script type="text/javascript">
let username = "{{ username }}";
let mobile = "{{ mobile }}";
let email = "{{ email }}";
let email_active = "{{ email_active }}";
</script>
<script type="text/javascript" src="{% static 'js/common.js' %}"></script>
<script type="text/javascript" src="{% static 'js/user_center_info.js' %}"></script>
</body>
</html>
个人用户中心前端界面静态文件:static/js/user_center_info.js
// 个人用户中心前端界面静态文件:static/js/user_center_info.js
let vm = new Vue({
el: '#app',
delimiters: ['[[', ']]'],
data: {
username: username,
mobile: mobile,
email: email,
email_active: email_active,
set_email: false,
error_email: false,
send_email_btn_disabled: false,
send_email_tip: '重新发送验证邮件',
histories: [],
},
mounted() {
// 邮箱是否激活:将Python的bool数据转成JS的bool数据
this.email_active = (this.email_active=='True') ? true : false;
// 是否在设置邮箱
this.set_email = (this.email=='') ? true : false;
// 请求浏览历史记录
// this.browse_histories();
},
methods: {
// 检查email格式
check_email(){
let re = /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/;
if (re.test(this.email)) {
this.error_email = false;
} else {
this.error_email = true;
}
},
// 取消保存
cancel_email(){
this.email = '';
this.error_email = false;
},
// 保存email
save_email(){
// 检查email格式
this.check_email();
if (this.error_email == false) {
let url = '/emails/';
axios.put(url, {
email: this.email
}, {
headers: {
'X-CSRFToken':getCookie('csrftoken')
},
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
this.set_email = false;
this.send_email_btn_disabled = true;
this.send_email_tip = '已发送验证邮件';
} else if (response.data.code == '4101') {
location.href = '/login/?next=/info/';
} else {
console.log(response);
}
})
.catch(error => {
console.log(error.response);
});
}
},
// 请求浏览历史记录
browse_histories(){
let url = '/browse_histories/';
axios.get(url, {
responseType: 'json'
})
.then(response => {
this.histories = response.data.skus;
for(let i=0; i<this.histories.length; i++){
this.histories[i].url = '/detail/' + this.histories[i].id + '/';
}
})
.catch(error => {
console.log(error.response);
})
},
}
});
需求:
- 当用户登录后,才能访问用户中心。
- 如果用户未登录,就不允许访问用户中心,将用户引导到登录界面。
实现方案: - 需要判断用户是否登录。
- 根据是否登录的结果,决定用户是否可以访问用户中心。
is_authenticate 判断用户是否登录
介绍:
- Django用户认证系统提供了方法request.user.is_authenticated()来判断用户是否登录。
- 如果通过登录验证则返回True。反之,返回False。
- 缺点:登录验证逻辑很多地方都需要,所以该代码需要重复编码好多次。
class UserInfoView(View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
if request.user.is_authenticated():
return render(request, 'user_center_info.html')
else:
return redirect(reverse('users:login'))
login_required装饰器 判断用户是否登录
- Django用户认证系统提供了装饰器login_required来判断用户是否登录。
- 内部封装了is_authenticate
- 位置:django.contrib.auth.decorators
- 如果通过登录验证则进入到视图内部,执行视图逻辑。
- 如果未通过登录验证则被重定向到LOGIN_URL配置项指定的地址。
- 如下配置:表示当用户未通过登录验证时,将用户重定向到登录页面。
LOGIN_URL = '/login/'
装饰as_view()方法返回值
url(r'^info/$', login_required(views.UserInfoView.as_view()), name='info'),
class UserInfoView(View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
定义View子类封装login_required装饰器
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(View):
"""验证用户是否登陆"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(LoginRequired):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
定义obejct子类封装login_required装饰器
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(object):
"""验证用户是否登陆"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(LoginRequired, View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
定义验证用户是否登录扩展类
class LoginRequiredMixin(object):
"""验证用户是否登录扩展类"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义的as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(LoginRequiredMixin, View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
登录时LoginRequiredMixin类中next参数的使用
开发环境配置文件:dev.py
# 开发环境配置文件:dev.py
# 判断用户是否登录后,指定未登录用户的重定向地址, LoginRequiredMixin类中的参数
LOGIN_URL = '/users/login/'
apps/users/views.py
文件,用户后端验证视图文件
"""
视图文件
apps/users/views.py文件,用户后端验证视图文件
"""
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse, JsonResponse
from django.views import View
from .forms import RegisterForm, LoginForm
from .models import User
from django.contrib.auth import login, logout, authenticate # authenticate封装的验证用户名和密码是否正确的方法
from django_redis import get_redis_connection
from django.contrib.auth.mixins import LoginRequiredMixin # 验证用户是否登录的类
# 个人用户中心
class UserInfoView(LoginRequiredMixin, View):
"""用户个人中心"""
def get(self, request):
"""提供用户个人中心"""
'''
login_url = None
permission_denied_message = ''
raise_exception = False
redirect_field_name = REDIRECT_FIELD_NAME
'''
# 验证用户是否登陆
# if request.user.is_authenticated:
# return render(request, 'user_center_info.html')
# else:
# return redirect(reverse('users:login')) # 用户未登录,跳转至登陆界面
# 上面的代码后期需要复用多次,可以引入LoginRequiredMixin类封装的方法和REDIRECT_FIELD_NAME = 'next'参数来重定向
return render(request, 'user_center_info.html') # 重定向到个人中心
# 退出登录
class LogoutView(View):
"""退出登陆逻辑实现"""
def get(self, request):
"""实现用户退出登录的功能"""
# 清除状态保持信息
logout(request)
# 退出登录之后重定向到首页
response = redirect(reverse('contents:index'))
# 删除cookies中的用户名
# result.set_cookie('username', user.username, max_age=3600*24*14) # 保存两周
response.delete_cookie('username')
return response # 响应结果
# 用户登陆
class LoginView(View):
"""用户名登陆"""
def get(self, request):
""" 提供登陆界面
:return: 登陆界面
"""
return render(request, 'login.html')
def post(self, request):
"""
实现登录逻辑
:param request: 请求对象
:return: 登录结果
"""
# 接受参数
login_form = LoginForm(request.POST)
# 校验参数
if login_form.is_valid():
# 接收参数
username = login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
remembered = request.POST.get('remembered') # 没经过form验证,使用request接收参数
# 认证登录用户
# users = User.objects.get(username=username)
# users.check_password(password) # check_password验证密码封装的方法,返回值bool类型
""" authenticate方法源码
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
"""
user = authenticate(username=username, password=password) # 重构authenticate方法之后,可以验证手机号登录和用户名登录
if user is None:
# 用户名或者密码输入错误
return render(request, 'login.html', {"errmsg": "用户名或者密码输入错误"})
# 实现状态保持
login(request, user)
# 设置状态保持的周期
if remembered != 'on':
# 没选中记住密码,浏览器关闭就需要销毁session信息
request.session.set_expiry(0) # set_expiry过期时间
else:
# 选中记住密码,session信息默认保存两周
# request.session.set_expiry(60*60*24*14)
request.session.set_expiry(None)
# REDIRECT_FIELD_NAME = 'next' LoginRequiredMixin类中源码的参数 ,用于获取登陆前的路由请求,方便登陆后直接定向到之前的请求界面
next = request.GET.get('next') # 获取url中的‘next’字符串参数
if next:
result = redirect(next) # 如果存在next参数,则重定向到这个地址
else:
# 后端将用户信息存入cookie
result = redirect(reverse('contents:index')) # redirect方法源码中会返回一个redirect_class
# set_cookie('key', 'value', 'erpriy') erpriy过期时间
result.set_cookie('username', user.username, max_age=3600*24*14) # 保存两周
# 响应登录结果 跳转到首页
return result
else:
print(login_form.errors.get_json_data())
context = {
"form_errors": login_form.errors,
}
return render(request, 'login.html', context=context)
class RegisterView(View):
"""用户注册"""
def get(self, request):
"""提供用户的注册界面"""
return render(request, 'register.html')
def post(self, request):
"""提供用户的注册逻辑"""
# 前端用户提交数据
form = RegisterForm(request.POST)
if form.is_valid():
# 接收参数
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
mobile = form.cleaned_data.get('mobile')
sms_code_client = request.POST.get('sms_code') # 验证短信验证码 sms_code是register.html 文件中命名的
# 判断用户输入的短信验证码是否正确
redis_conn = get_redis_connection('verify_code') # 链接redis中配置的数据库
sms_code_server = redis_conn.get('sms_%s' % mobile) # 根据存储时候的格式写
if sms_code_server is None:
return render(request, 'register.html', {'sms_code_errmsg': '短信验证码已经失效'}) # 错误信息渲染到前端界面
if sms_code_server.decode() != sms_code_client: # sms_code_server数据类型需要转换
return render(request, 'register.html', {'sms_code_errmsg': '短信验证码填写错误'})
try:
# user = User(username=username, password=password, mobile=mobile)
# 下面的添加数据的方法是封装了加密等功能的函数,更安全
users = User.objects.create_user(username=username, password=password, mobile=mobile)
except: # 如果保存数据失败
return render(request, 'register.html', {'register_error_message': '注册失败'})
# 保持用户登录的状态
login(request, users)
# 返回响应
# return HttpResponse('success')
return redirect(reverse('contents:index')) # 注册成功,跳转到首页
else:
print(form.errors.get_json_data())
# return HttpResponse("fail")
# 返回注册错误信息到前端界面
context = {
'form_error': form.errors,
}
return render(request, 'register.html', context=context)
# 判断用户名是否已经存在
class UsernameExists(View):
""" 判断用户名是否已经存在"""
def get(self, request, username): # username用户名
count = User.objects.filter(username=username).count() # 查询数据库中信息
return JsonResponse({"code": 0, "errmsg": "OK", "count": count}) # 返回给前端界面
3、QQ登录
QQ登录开发文档
QQ登录:即我们所说的第三方登录,是指用户可以不在本项目中输入密码,而直接通过第三方的验证,成功登录本项目。
QQ互联开发者申请步骤
若想实现QQ登录,需要成为QQ互联的开发者,审核通过才可实现。
- 相关连接:
http://wiki.connect.qq.com/%E6%88%90%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85
QQ互联应用申请步骤
成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID。
- 相关连接:
http://wiki.connect.qq.com/__trashed-2
网站对接QQ登录步骤
QQ互联提供有开发文档,帮助开发者实现QQ登录。
- 相关连接:
http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
QQ登录流程分析
定义QQ登录模型类
QQ登录成功后,我们需要将QQ用户和商场用户关联到一起,方便下次QQ登录时使用,所以我们选择使用MySQL数据库进行存储。
定义模型类基类
为了给项目中模型类补充数据创建时间和更新时间两个字段,我们需要定义模型类基类。
抽象模型类文件,用于继承使用:utils/models.py
# -*- encoding: utf-8 -*-
"""
@File : models.py
@Time : 2020/8/12 21:53
@Author : chen
抽象模型类文件,用于继承使用:utils/models.py
"""
from django.db import models
class BaseModel(models.Model):
# auto_now_add=True第一次添加时间
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
# auto_now=True最近一次更新的时间
update_time = models.DateTimeField(auto_now=True, verbose_name='创建时间')
class Meta:
# 数据库迁移时候不会被创建
abstract = True # 说明是抽象模型类, 用于继承使用,数据库迁移时不会创建BaseModel的表
在oauth/models.py中定义QQ身份(openid)与用户模型类User的关联关系
第三方登陆QQ的模型文件:apps/oauth/models.py
"""
第三方登陆QQ的模型文件:apps/oauth/models.py
"""
from django.db import models
from utils.models import BaseModel
# Create your models here.
class OAuthQQUser(BaseModel):
# ForeignKey('users.User') 外键关联User模型,on_delete=models.CASCADE级联删除
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用户')
openid = models.CharField(max_length=100, verbose_name='openid') # qq用户登录后的id
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登陆用户数据'
verbose_name_plural = verbose_name
映射到数据库中
定义QQ登录模型类
创建一个新的应用oauth,用来实现QQ第三方认证登录。
'''
项目总路由文件:shop/urls.py
'''
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')), # 如果当时注册users模块时候没有使用sys.path.insert导入路径,这里就需要改为 'apps.users.urls'
path('', include('contents.urls')), # 首页路由
path('', include('verifications.urls')), # 验证码路由
path('', include('oauth.urls')), # QQ登陆路由
]
oauth模块中的路由文件: apps/oauth/urls.py
# -*- encoding: utf-8 -*-
"""
@File : urls.py
@Time : 2020/8/12 22:05
@Author : chen
oauth模块中的路由文件: apps/oauth/urls.py
"""
from django.urls import path, include
from . import views
app_name = 'oauth'
urlpatterns = [
]
4、QQ登录工具QQLoginTool
QQLoginTool介绍
- 该工具封装了QQ登录时对接QQ互联接口的请求操作。可用于快速实现QQ登录。
QQLoginTool安装
pip install QQLoginTool
QQLoginTool使用说明
1.导入
from QQLoginTool.QQtool import OAuthQQ
2.初始化OAuthQQ对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)
3.获取QQ登录扫码页面,扫码后得到Authorization Code
login_url = oauth.get_qq_url()
4.通过Authorization Code获取Access Token
access_token = oauth.get_access_token(code)
5.通过Access Token获取OpenID
openid = oauth.get_open_id(access_token)
OAuth2.0认证获取openid
待处理业务逻辑
# 提取code请求参数
# 使用code向QQ服务器请求access_token
# 使用access_token向QQ服务器请求openid
# 使用openid查询该QQ用户是否在美多商城中绑定过用户
# 如果openid已绑定美多商城用户,直接生成JWT token,并返回
# 如果openid没绑定美多商城用户,创建用户并绑定到openid
获取QQ登录扫码页面
1.请求方式
2.请求参数:查询参数
3.响应结果:JSON
项目代码如下:
第三方QQ登陆视图文件: apps/oauth/views.py
"""
第三方QQ登陆视图文件
apps/oauth/views.py
"""
from django.shortcuts import render
from django.views import View
from QQLoginTool.QQtool import OAuthQQ # 第三方QQ登陆的类
from django.conf import settings
from django import http
from utils.response_code import RETCODE # 响应代码
# QQ回调用户视图
class QQAuthUserView(View):
def get(self, request):
pass
# 第三方QQ登陆视图
class QQAuthURLView(View):
"""提供QQ登陆的扫码页面"""
"""
提供QQ登录页面网址
https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx
"""
def get(self, request):
# next表示从哪个页面进入到的登录页面,将来登录成功后,就自动回到那个页面
next = request.GET.get('next')
# 获取QQ登录页面网址
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI, state=next)
# 生成QQ登陆的扫码链接
login_url = oauth.get_qq_url()
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'login_url': login_url})
开发环境配置文件:dev.py
# QQ登陆设置参数
QQ_CLIENT_ID = '101518219'
QQ_CLIENT_SECRET = '418d84ebdc7241efb79536886ae95224'
QQ_REDIRECT_URI = 'http://www.meiduo.site:8000/oauth_callback'
接收Authorization Code
提示:
- 用户在QQ登录成功后,QQ会将用户重定向到我们配置的回调网址。
- 在QQ重定向到回调网址时,会传给我们一个
Authorization Code
。 - 我们需要拿到
Authorization Code
并完成OAuth2.0认证获取openid。 - 在本项目中,我们申请QQ登录开发资质时配置的回调网址为:
http://www.meiduo.site:8000/oauth_callback
- QQ互联重定向的完整网址为:
http://www.meiduo.site:8000/oauth_callback/?code=AE263F12675FA79185B54870D79730A7&state=%2F
oauth模块中的路由文件: apps/oauth/urls.py
# -*- encoding: utf-8 -*-
"""
@File : urls.py
@Time : 2020/8/12 22:05
@Author : chen
oauth模块中的路由文件: apps/oauth/urls.py
"""
from django.urls import path, include
from . import views
app_name = 'oauth'
urlpatterns = [
path('qq/login/', views.QQAuthURLView.as_view()), # 定义qq登陆路由 提供QQ登陆的扫码页面
# 处理QQ登陆后的回调
path('oauth_callback/', views.QQAuthUserView.as_view()),
]
5、本机绑定域名
Windows系统
编辑 C:\Windows\System32\drivers\etc\hosts
ubuntu系统或者Mac系统
编辑 /etc/hosts
同时需要配置开发设置文件:
开发环境配置文件:dev.py
# 开发环境配置文件:dev.py
# 绑定本机域名
ALLOWED_HOSTS = ['www.meiduo.site']