Django实战项目-03用户中心的开发

概览

  • 开发user_center_info、user_center_order、user_center_site三个页面,三个页面都属于apps中的user模块,其中user_center_info默认第一个显示,三个页面可以通过页面左侧的选项卡随时切换。
  • 上述的三个应该在用户登录后才能继续访问,应该需要对用户是否登录进行校验,若未登录,则返回login页面登录。
  • templates中页面代码重复的部分用模板继承进行改写,简化代码。
  • 浏览记录的设计:用redis存储用户的访问信息,提高效率。

注销当前用户

apps/user/urls.py

url(r'^logout$', LogoutView.as_view(), name='logout'), # 注销

apps/user/views.py

class LogoutView(View):
    def get(self, request):
        logout(request)    # 认证系统的logout的方法,不需要手动注销

        return redirect(reverse('goods:index'))

用户中心的登录校验

登录校验的方法一:在urls.py相关的函数前面,用login_required修饰。

# 用户中心登录校验的方式一:在前面添加login_required
    # url(r'^$', login_required(UserInfoView.as_view()), name='user'), # 用户中心-信息页(默认的第一个页面)
    # url(r'^order$', login_required(UserOrderView.as_view()), name='order'), # 用户中心-订单页
    # url(r'^address$', login_required(AddressView.as_view()), name='address'), # 用户中心-地址页

方法二:定义登录校验的相关类,urls中保持不变,在类中继承相关类。

# 方式二,装饰类的方式处理,定义在utils包中
    url(r'^$', UserInfoView.as_view(), name='user'),  # 用户中心-信息页
    url(r'^order$', UserOrderView.as_view(), name='order'),  # 用户中心-订单页
    url(r'^address$', AddressView.as_view(), name='address'),  # 用户中心-地址页

新建utils包,mixin.py代码如下:

from django.contrib.auth.decorators import login_required


class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **initkwargs):
        # 调用父类的as_view
        view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
        return login_required(view)

视图代码中的类继承这个类:

class UserOrderView(LoginRequiredMixin, View):

用户中心视图层代码

当未登录访问用户中心页面时,跳转到登录页面,url后面会带上一个next参数,我们需要把next参数设置成自己的路径,便于登录后直接访问之前想访问的地址。

项目settings文件配置:

# 配置登录url地址
LOGIN_URL='/user/login' # /accounts/login

为了能够登录后跳转到用户中心相关页面,需要对登录函数进行修改:

# /user/login
class LoginView(View):
    '''登录'''
    def get(self, request):
        '''显示登录页面'''
        # 判断是否记住了用户名
        if 'username' in request.COOKIES:
            username = request.COOKIES.get('username')
            checked = 'checked'
        else:
            username = ''
            checked = ''

        # 使用模板
        return render(request, 'login.html', {'username':username, 'checked':checked})

    def post(self, request):
        '''登录校验'''
        # 接收数据
        username = request.POST.get('username')
        password = request.POST.get('pwd')

        # 校验数据
        if not all([username, password]):
            return render(request, 'login.html', {'errmsg':'数据不完整'})

        # 业务处理:登录校验 authenticate是认证系统提供的方法
        user = authenticate(username=username, password=password)
        if user is not None:
            # 用户名密码正确
            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
            else:
                # 用户未激活
                return render(request, 'login.html', {'errmsg':'账户未激活'})
        else:
            # 用户名或密码错误
            return render(request, 'login.html', {'errmsg':'用户名或密码错误'})

用户中心三个视图层代码如下,其中的page参数是为了根据页面的不同,在左侧给对应的选项卡加上class:active

class UserInfoView(View):
    '''用户中心-信息页'''
    def get(self, request):
        # Django会给request对象添加一个属性request.user
        # 如果用户未登录->user是AnonymousUser类的一个实例对象
        # 如果用户登录->user是User类的一个实例对象
        # request.user.is_authenticated()

        # 获取用户个人信息
        user = request.user
        address = Address.objects.get_default_address(user)

        # 获取用户的历史浏览记录
        # from redis import StrictRedis
        # sr = StrictRedis(host='172.16.179.130', port='6379', db=9)
        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)

        # 遍历获取用户浏览的商品信息,这里之所以要这样的做的原因是因为,即使查询的是[2,3,1],但是数据库返回的顺序是ID从小到大的
        goods_li = []
        for id in sku_ids:
            goods = GoodsSKU.objects.get(id=id)
            goods_li.append(goods)

        # 用户中心的三个页面,info/order/site只有右侧的内容不同,但是左侧选项卡需要
        # class:active来指定,所以这里给模板的templates需要带上page属性,便于分辨是哪一页
        context = {'page': 'user',
                   'address': address,
                   'goods_li': goods_li,
                   }

        return render(request, 'user_center_info.html', context)


class UserOrderView(LoginRequiredMixin, View):
    '''用户中心-订单页'''
    def get(self, request):
        '''显示'''
        # 获取用户的订单信息
        return render(request, 'user_center_order.html', {'page': 'order'})


class AddressView(LoginRequiredMixin, View):
    '''用户中心-地址页'''
    def get(self, request):
        '''显示'''
        # 获取登录用户对应User对象
        user = request.user

        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
        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请求方式

其中地址中的get_default_address方法,是创建了模型管理器类后的方法,代码如下:

from django.db import models
from django.contrib.auth.models import AbstractUser
from db.base_model import BaseModel
# Create your models here.


class User(AbstractUser, BaseModel):
    '''用户模型类'''

    class Meta:
        db_table = 'df_user'
        verbose_name = '用户'
        verbose_name_plural = verbose_name


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)
        except self.model.DoesNotExist:
            # 不存在默认收获地址
            address = None

        return address


class Address(BaseModel):
    '''地址模型类'''
    user = models.ForeignKey('User', verbose_name='所属账户')
    receiver = models.CharField(max_length=20, verbose_name='收件人')
    addr = models.CharField(max_length=256, verbose_name='收件地址')
    zip_code = models.CharField(max_length=6, null=True, verbose_name='邮政编码')
    phone = models.CharField(max_length=11, verbose_name='联系电话')
    is_default = models.BooleanField(default=False, verbose_name='是否默认')

    # 自定义的模型管理器对象
    objects = AddressManager()

    class Meta:
        db_table = 'df_address'
        verbose_name = '地址'
        verbose_name_plural = verbose_name

 templates代码:

父模板  base_user_center.html

{# 用户中心3页面 #}
{% extends 'base_no_cart.html' %}
{% block title %}天天生鲜-用户中心{% endblock title %}
{% block page_title %}用户中心{% endblock page_title %}
{% block body %}
    <div class="main_con clearfix">
		<div class="left_menu_con clearfix">
			<h3>用户中心</h3>
			<ul>
				<li><a href="{% url 'user:user' %}" {% if page == 'user' %}class="active"{% endif %}>· 个人信息</a></li>
				<li><a href="{% url 'user:order' %}" {% if page == 'order' %}class="active"{% endif %}>· 全部订单</a></li>
				<li><a href="{% url 'user:address' %}" {% if page == 'address' %}class="active"{% endif %}>· 收货地址</a></li>
			</ul>
		</div>
        {# 用户中心右侧内容块 #}
        {% block right_content %}{% endblock right_content %}
    </div>
{% endblock body %}

user_center_info.html

{% extends 'base_user_center.html' %}
{% block right_content %}
		<div class="right_content clearfix">
				<div class="info_con clearfix">
				<h3 class="common_title2">基本信息</h3>
						<ul class="user_info_list">
							<li><span>用户名:</span>{{ user.username }}</li>
                            {% if address %}
                                <li><span>联系方式:</span>{{ address.phone }}</li>
                                <li><span>联系地址:</span>{{ address.addr }}</li>
                            {% else %}
                                <li><span>联系方式:</span>无默认</li>
							    <li><span>联系地址:</span>无默认</li>
                            {% endif %}
						</ul>
				</div>
				
				<h3 class="common_title2">最近浏览</h3>
				<div class="has_view_list">
					<ul class="goods_type_list clearfix">
                        {% for goods in goods_li %}
				        <li>
                            <a href="detail.html"><img src="{{ goods.image.url }}"></a>
                            <h4><a href="detail.html">{{ goods.name }}</a></h4>
                            <div class="operate">
                                <span class="prize">¥{{ goods.price }}</span>
                                <span class="unit">{{ goods.price }}/{{ goods.unite }}</span>
                                <a href="#" class="add_goods" title="加入购物车"></a>
                            </div>
                        </li>
                        {% empty %}
                            无历史浏览记录
                        {% endfor %}
			        </ul>
		        </div>
		</div>
{% endblock right_content %}

user_center_order.html

{% extends 'base_user_center.html' %}
{% block right_content %}
		<div class="right_content clearfix">
				<h3 class="common_title2">全部订单</h3>
				<ul class="order_list_th w978 clearfix">
					<li class="col01">2016-8-21 17:36:24</li>
					<li class="col02">订单号:56872934</li>
					<li class="col02 stress">未支付</li>		
				</ul>

				<table class="order_list_table w980">
					<tbody>
						<tr>
							<td width="55%">
								<ul class="order_goods_list clearfix">					
									<li class="col01"><img src="images/goods02.jpg"></li>
									<li class="col02">嘎啦苹果嘎啦苹果<em>11.80元/500g</em></li>	
									<li class="col03">1</li>
									<li class="col04">11.80元</li>	
								</ul>
								<ul class="order_goods_list clearfix">					
									<li class="col01"><img src="images/goods02.jpg"></li>
									<li class="col02">嘎啦苹果嘎啦苹果<em>11.80元/500g</em></li>	
									<li class="col03">1</li>
									<li class="col04">11.80元</li>	
								</ul>
							</td>
							<td width="15%">33.60元</td>
							<td width="15%">待付款</td>
							<td width="15%"><a href="#" class="oper_btn">去付款</a></td>
						</tr>
					</tbody>
				</table>
				
				<ul class="order_list_th w978 clearfix">
					<li class="col01">2016-8-21 17:36:24</li>
					<li class="col02">订单号:56872934</li>
					<li class="col02 stress">已支付</li>			
				</ul>
				<table class="order_list_table w980">
					<tbody>
						<tr>
							<td width="55%">
								<ul class="order_goods_list clearfix">					
									<li class="col01"><img src="images/goods02.jpg"></li>
									<li class="col02">嘎啦苹果嘎啦苹果<em>11.80元/500g</em></li>	
									<li class="col03">1</li>
									<li class="col04">11.80元</li>	
								</ul>
								<ul class="order_goods_list clearfix">					
									<li class="col01"><img src="images/goods02.jpg"></li>
									<li class="col02">嘎啦苹果嘎啦苹果<em>11.80元/500g</em></li>	
									<li class="col03">1</li>
									<li class="col04">11.80元</li>	
								</ul>
							</td>
							<td width="15%">33.60元</td>
							<td width="15%">已付款</td>
							<td width="15%"><a href="#" class="oper_btn">查看物流</a></td>
						</tr>
					</tbody>
				</table>

				<div class="pagenation">
					<a href="#"><上一页</a>
					<a href="#" class="active">1</a>
					<a href="#">2</a>
					<a href="#">3</a>
					<a href="#">4</a>
					<a href="#">5</a>
					<a href="#">下一页></a>
				</div>
		</div>
{% endblock right_content %}

user_center_cite.html 

注意点:

  • 表单提交记得加上{% csrf_token %}
  • 表单提交没有指定提交地址时,会给当前地址栏中的url提交。
{% extends 'base_user_center.html' %}
{% block right_content %}
		<div class="right_content clearfix">
				<h3 class="common_title2">收货地址</h3>
				<div class="site_con">
					<dl>
						<dt>当前地址:</dt>
                        {% if address %}
						    <dd>{{ address.addr }} ({{ address.receiver }} 收) {{ address.phone }}</dd>
					    {% else %}
                            <dd>无默认地址</dd>
                        {% endif %}
                    </dl>
				</div>
				<h3 class="common_title2">编辑地址</h3>
				<div class="site_con">
					<form method="post">
                        {% csrf_token %}
						<div class="form_group">
							<label>收件人:</label>
							<input type="text" name="receiver">
						</div>
						<div class="form_group form_group2">
							<label>详细地址:</label>
							<textarea class="site_area" name="addr"></textarea>
						</div>
						<div class="form_group">
							<label>邮编:</label>
							<input type="text" name="zip_code">
						</div>
						<div class="form_group">
							<label>手机:</label>
							<input type="text" name="phone">
						</div>

						<input type="submit" value="提交" class="info_submit">
					</form>
				</div>
		</div>
{% endblock right_content %}

历史记录的实现

  • 采用第三方包django-redis包来是实现,注意两个版本4.7.0和3.8.4,安装时候很坑,会把相关的django和redis包安装到最新版本,这里采用3.8.4的版本,安装过程中发现了一个现象,电脑中的redis版本和python环境的中的redis版本不一定要统一,如:电脑中的是redis2.8.0,而为了兼容django-redis,pip安装了redis2.10.0,发现功能还是可以正常使用的。

settings.py

# Django的缓存配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/9",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

# 配置session存储
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

历史记录实现:

  • 最新访问的页面肯定第一个显示,因此需要保证数据库查询的顺序,同时在插入的时候,需要保证在左侧插入,需要用lpush方法,记录的过程在后面实现。
  • 采用单用户对应访问列表的方式,键是用户,值是商品的id。
# 获取用户的历史浏览记录
        # from redis import StrictRedis
        # sr = StrictRedis(host='172.16.179.130', port='6379', db=9)
        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)

        # 遍历获取用户浏览的商品信息,这里之所以要这样的做的原因是因为,即使查询的是[2,3,1],但是数据库返回的顺序是ID从小到大的
        goods_li = []
        for id in sku_ids:
            goods = GoodsSKU.objects.get(id=id)
            goods_li.append(goods)

        # 用户中心的三个页面,info/order/site只有右侧的内容不同,但是左侧选项卡需要
        # class:active来指定,所以这里给模板的templates需要带上page属性,便于分辨是哪一页
        context = {'page': 'user',
                   'address': address,
                   'goods_li': goods_li,
                   }

        return render(request, 'user_center_info.html', context)

对应的template:

<h3 class="common_title2">最近浏览</h3>
				<div class="has_view_list">
					<ul class="goods_type_list clearfix">
                        {% for goods in goods_li %}
				        <li>
                            <a href="detail.html"><img src="{{ goods.image.url }}"></a>
                            <h4><a href="detail.html">{{ goods.name }}</a></h4>
                            <div class="operate">
                                <span class="prize">¥{{ goods.price }}</span>
                                <span class="unit">{{ goods.price }}/{{ goods.unite }}</span>
                                <a href="#" class="add_goods" title="加入购物车"></a>
                            </div>
                        </li>
                        {% empty %}
                            无历史浏览记录
                        {% endfor %}
			        </ul>
		        </div>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值