从零开始搭建五脏俱全的django项目!day02
接下来我们需要配置用户信息页面的相关配置
首先需要在user.urls中添加
from django.contrib.auth.decorators import login_required
# login_required Django认证系统自带的验证用户是否登录
from user.views import RegisterView, ActiveView, LoginView, LogoutView, UserInfoView, UserOrderView, AddressView
url(r'^$', login_required(UserInfoView.as_view()), name='user'), # 用户中心-信息页
# login_required函数传入一个处理类.as_view(),在调用这个处理类之前,login_required会先检测用户有没有登录如果有用户登录的话会正常调用处理类,如果没有的话,会回调到另一个地址
url(r'^order$', login_required(UserOrderView.as_view()), name='order'), # 用户中心-订单页
url(r'^address$', login_required(AddressView.as_view()), name='address'), # 用户中心-地址页
因为当login_reuqired没有检测到用户登录时会重定向到settings.LOGIN_URL,并将当前访问的绝对路径传递到查询字符串中。例如:/accounts/login/?next=/polls/3/。
所以我们要在settins中修改LOGIN_URL
# 配置当login_required检测到用户未登录时,跳转到的页面
LOGIN_URL = '/user/login'
当用户登录后需要有一个回调地址,观察url地址,会发现?后有参数next其中封装了,请求原地址,所以在LoginView中修改对post的处理
if user.is_active:
# 用户已激活
# 记录用户的登录状态
login(request, user)
# 获取登录后所要跳转到的地址
# 默认跳转到首页
next_url = request.GET.get('next', reverse('goods:index'))
# 跳转到next_url
response = redirect(next_url) # HttpResponseRedirect
# 判断是否需要记住用户名
remember = request.POST.get('remember')
if remember == 'on':
# 记住用户名
response.set_cookie('username', username, max_age=7*24*3600)
else:
response.delete_cookie('username')
# 返回response
return response
因为考虑到要检测用户是否登录的页面和情况很多所以在这里用类封装处理,减少代码量,考虑到用login_required的app.urls可能会很多所以在项目主目录下,创建python package utils,在utils下创建mixin.py
类封装处理流程
1.首先封装LoginRequired的Mixin类中的as_view类方法
from django.contrib.auth.decorators import login_required
# login_required Django认证系统自带的验证用户是否登录
class LoginRequiredMixin(object):
@classmethod
def as_view(cls, **initkwargs):
"""
调用父类的as_view
:param initkwargs:
:return:
"""
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
# 等同于View.as_view()
# 类封装减少了代码量
return login_required(view)
2.然后在要进行是否有用户登录的请求处理类,中继承LoginRequiredMixin
class UserInfoView(LoginRequiredMixin, View):
用户登录后需要显示欢迎信息因为用到了login_required所以当用户登录后会在Django使用会话和中间件来拦截request 对象到认证系统中。它们在每个请求上提供一个request.user属性,表示当前的用户。如果当前的用户没有登入,该属性将设置成AnonymousUser的一个实例,否则它将是User的实例。你可以通过is_authenticated()区分它们,像这样:
if request.user.is_authenticated():
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
当你用户登录后渲染模板时除了你给模板文件传递的模板变量之外,django框架会把request.user也传给模板文件所以可以在模板上利用如下语法判断是否有用户登录
{% if user.is_authenticated %}
欢迎您:{{ user.username }}
|
退出
{% else %}
{% endif %}
接下来处理用户详情页
首先处理地址页面
# /user/address
class AddressView(LoginRequiredMixin, View):
'''用户中心-地址页'''
def get(self, request):
'''显示'''
# 获取登录用户对应User对象
user = request.user
# 获取用户的默认收货地址
try:
address = Address.objects.get(user=user, is_default=True) # models.Manager
except Address.DoesNotExist:
# 不存在默认收货地址
address = None
# address = Address.objects.get_default_address(user)
# 使用模板
return render(request, 'user_center_site.html', {'page':'address', 'address':address})
def post(self, request):
'''地址的添加'''
# 接收数据
receiver = request.POST.get('receiver')
addr = request.POST.get('addr')
zip_code = request.POST.get('zip_code')
phone = request.POST.get('phone')
# 校验数据
if not all([receiver, addr, phone]):
return render(request, 'user_center_site.html', {'errmsg':'数据不完整'})
# 校验手机号
if not re.match(r'^1[3|4|5|7|8][0-9]{9}$', phone):
return render(request, 'user_center_site.html', {'errmsg':'手机格式不正确'})
# 业务处理:地址添加
# 如果用户已存在默认收货地址,添加的地址不作为默认收货地址,否则作为默认收货地址
# 获取登录用户对应User对象
user = request.user
# try:
# address = Address.objects.get(user=user, is_default=True)
# except Address.DoesNotExist:
# # 不存在默认收货地址
# address = None
address = Address.objects.get_default_address(user)
if address:
is_default = False
else:
is_default = True
# 添加地址
Address.objects.create(user=user,
receiver=receiver,
addr=addr,
zip_code=zip_code,
phone=phone,
is_default=is_default)
# 返回应答,刷新地址页面
return redirect(reverse('user:address')) # get请求方式
自定义objects模型管理器对象的两种应用场景:
1.改变原有查询的结果集 all()
2.封装用于操作模型类对应的数据表CRUD
观察以上代码发现在get,post请求中有重复的查询默认地址语句所以重写Address的objects(模型管理器)
class AddressManager(models.Manager):
'''地址模型管理器类'''
# 1.改变原有查询的结果集:all()
# 2.封装方法:用户操作模型类对应的数据表(增删改查)
def get_default_address(self, user):
'''获取用户默认收货地址'''
# self.model:获取self对象所在的模型类
try:
address = self.get(user=user, is_default=True) # models.Manager
except self.model.DoesNotExist:
# 不存在默认收货地址
address = None
return address
然后就可以直接在address中使用以下代码验证
address = Address.objects.get_default_address(user)
处理用户信息页在user.urls中添加
url(r'^$', UserInfoView.as_view(), name='user'), # 用户中心-信息页
在user.views中
# /user
class UserInfoView(LoginRequiredMixin, View):
'''用户中心-信息页'''
def get(self, request):
'''显示'''
# Django会给request对象添加一个属性request.user
# 如果用户未登录->user是AnonymousUser类的一个实例对象
# 如果用户登录->user是User类的一个实例对象
# request.user.is_authenticated()
# 如果is User对象返回True
# else return False
# 获取用户的个人信息
user = request.user
address = Address.objects.get_default_address(user)
# 组织上下文
context = {'page':'user',
'address':address,}
# 除了你给模板文件传递的模板变量之外,django框架会把request.user也传给模板文件
return render(request, 'user_center_info.html', context)
接下来因为用户中心信息页面需要显示浏览记录所以当用户访问商品详情页面时添加浏览记录,访问用户中心个人信息页面时显示浏览记录
1.考虑到要频繁操作数据库所以选用redis存储历史浏览记录
2.redis存储浏览记录的格式(redis datastruct:list, string, hash, set, zset)
每个用户的历史浏览记录用一条数据保存:list
history_用户id:[2,3,1]
利用列表从左侧进行插入,以保证每次获取到的数据都是用户最新浏览商品id
获取历史浏览记录
在某些情况下你的应用需要进入原生 Redis 客户端使用一些 django cache 接口没有暴露出来的进阶特性. 为了避免储存新的原生连接所产生的另一份设置, django-redis 提供了方法 get_redis_connection(alias) 使你获得可重用的连接字符串
>>> from django_redis import get_redis_connection
>>> con = get_redis_connection("default")
>>> con
因为要在用户中心信息页面显示浏览记录所以修改views中的UserInfo处理类
# /user
class UserInfoView(LoginRequiredMixin, View):
'''用户中心-信息页'''
def get(self, request):
'''显示'''
# Django会给request对象添加一个属性request.user
# 如果用户未登录->user是AnonymousUser类的一个实例对象
# 如果用户登录->user是User类的一个实例对象
# request.user.is_authenticated()
# 如果is User对象返回True
# else return False
# 获取用户的个人信息
user = request.user
address = Address.objects.get_default_address(user)
# 获取用户的历史浏览记录
# from redis import StrictRedis
# sr = StrictRedis(host='127.0.0.1', port='6379', db=2)
con = get_redis_connection('default')
history_key = 'history_%d'%user.id
# 获取用户最新浏览的5个商品的id
sku_ids = con.lrange(history_key, 0, 4) # [2,3,1]
# 从数据库中查询用户浏览的商品的具体信息
# goods_li = GoodsSKU.objects.filter(id__in=sku_ids)
#
# goods_res = []
# for a_id in sku_ids:
# for goods in goods_li:
# if a_id == goods.id:
# goods_res.append(goods)
# 遍历获取用户浏览的商品信息
goods_li = []
for id in sku_ids:
goods = GoodsSKU.objects.get(id=id)
goods_li.append(goods)
# 组织上下文
context = {'page':'user',
'address':address,
'goods_li':goods_li}
# 除了你给模板文件传递的模板变量之外,django框架会把request.user也传给模板文件
return render(request, 'user_center_info.html', context)