zoukankan      html  css  js  c++  java
  • Django项目-----天天生鲜

    一、天天生鲜项目分析及架构

    1、电商模式

    天天生鲜项目属于B2C(Business to Customer)电商模式,即企业对个人。

    2、需求分析

    2.1 用户模块

    1) 注册页

    • l 注册时校验用户名是否已被注册。
    • l 完成用户信息的注册。
    • l 给用户的注册邮箱发送邮件,用户点击邮件中的激活链接完成用户账户的激活。

    2) 登录页

    • l 实现用户的登录功能。

    3) 用户中心

    • l 用户中心信息页:显示登录用户的信息,包括用户名、电话和地址,同时页面下方显示出用户最近浏览的商品信息。
    • l 用户中心地址页:显示登录用户的默认收件地址,页面下方的表单可以新增用户的收货地址。
    • l 用户中心订单页:显示登录用户的订单信息。

    4) 其他

    • l 如果用户已经登录,页面顶部显示登录用户的信息。

    2.2 商品相关

    1) 首页

    • l 动态指定首页轮播商品信息。
    • l 动态指定首页活动信息。
    • l 动态获取商品的种类信息并显示。
    • 动态指定首页显示的每个种类的商品(包括图片商品和文字商品)。
    • l 点击某一个商品时跳转到商品的详情页面。

    2) 商品详情页

    • l 显示出某个商品的详情信息。
    • 页面的左下方显示出该种类商品的2个新品信息。

    3)商品列表页

    • l 显示出某一个种类商品的列表数据,分页显示并支持按照默认、价格、和人气进行排序。
    • 页面的左下方显示出该种类商品的2个新品信息。

    4)其他

    • l 通过页面搜索框搜索商品信息。

    2.3 购物车相关

    • l 列表页和详情页将商品添加到购物车。
    • l 用户登录后,首页,详情页,列表页显示登录用户购物车中商品的数目。
    • l 购物车页面:对用户购物车中商品的操作。如选择某件商品,增加或减少购物车中商品的数目。

    2.4 订单相关

    • l 提交订单页面:显示用户准备购买的商品信息。
    • l 点击提交订单完成订单的创建。
    • l 用户中心订单页显示用户的订单信息。
    • l 点击支付完成订单的支付。

     

    3、项目架构

     4、数据表结构

     

     

    5、富文本编辑器

    借助富文本编辑器,网站的编辑人员能够像使用offfice一样编写出漂亮的、所见即所得的页面。此处以tinymce为例,其它富文本编辑器的使用也是类似的。

    在虚拟环境中安装包。

    pip install django-tinymce==2.6.0

    安装完成后,可以使用在Admin管理中,也可以自定义表单使用。

    示例

    1)在test6/settings.py中为INSTALLED_APPS添加编辑器应用。

    INSTALLED_APPS = (
        ...
        'tinymce',
    )
    配置setting.py

    2)在test6/settings.py中添加编辑器配置。

    TINYMCE_DEFAULT_CONFIG = {
        'theme': 'advanced',
        'width': 600,
        'height': 400,
    }
    配置setting.py

    3)在test6/urls.py中配置编辑器url。

    urlpatterns = [
        ...
        url(r'^tinymce/', include('tinymce.urls')),
    ]
    配置url

    6、项目架构

    二、各个模块功能的完成

    用户模块--user

    1、用户注册                                                                                                                                                                                                         

    用户注册的思路:用户通过注册模板文件提交了form表单到django后台,django中view函数接收这些参数,对参数进行校验,然后向用户发送邮件,用户通过点击邮件中的链接进行激活,view函数将数据保存到MySQL数据库中,其中调用celery异步队列处理发送邮件的任务。

    • (1)注册模板文件中的form表单
    <form action="/user/register" method="post">
                        {% 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>
                        {{ errmsg }}
                    </form>
    注册模板
    • (2) 注册视图函数
      1 from django.shortcuts import render,redirect
      2 from django.http import HttpResponse
      3 from django.urls import reverse
      4 from django.views.generic import View
      5 from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
      6 from itsdangerous import SignatureExpired
      7 from django.conf import settings
      8 from django.core.mail import send_mail
      9 import re
     10 from user.models import User
     11 from celery_tasks.tasks import send_register_active_email
     12 
     13 '''
     14 def register(request):
     15     ##  注册
     16     if request.method == 'GET':
     17         ##  注册页面
     18         return render(request,'register.html')
     19     else:
     20         ##  注册处理
     21         ##  1、接收数据
     22         username = request.POST.get('user_name')
     23         password = request.POST.get('pwd')
     24         email = request.POST.get('email')
     25         allow = request.POST.get('allow')
     26 
     27         ##  校验username在数据库中是否存在
     28         try:
     29             user = User.objects.get(username=username)
     30         except Exception as e:
     31             user = None
     32         if user:
     33             ##  用户名存在
     34             return render(request, 'register.html', {'errmsg': '用户已存在'})
     35 
     36         ##  2、校验数据
     37         if not all([username, password, email]):
     38             ##  数据不完整
     39             return render(request, 'register.html', {'errmsg': '数据不完整'})
     40         if not re.match('^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$', email):
     41             return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
     42         if allow != 'on':
     43             return render(request, 'register.html', {'errmsg': '请同意协议'})
     44 
     45         ##  3、保存进数据库
     46         user = User.objects.create_user(username, email, password)
     47         user.is_active = 0
     48         user.save()
     49 
     50         ##  4、返回应答
     51         return redirect('goods:index')
     52 
     53 def register_handle(request):
     54     ##  注册处理
     55     ##  1、接收数据
     56     username = request.POST.get('user_name')
     57     password = request.POST.get('pwd')
     58     email = request.POST.get('email')
     59     allow = request.POST.get('allow')
     60 
     61     ##  校验username在数据库中是否存在
     62     try:
     63         user = User.objects.get(username=username)
     64     except Exception as e:
     65         user = None
     66     if user:
     67         ##  用户名存在
     68         return render(request,'register.html',{'errmsg':'用户已存在'})
     69 
     70     ##  2、校验数据
     71     if not all([username,password,email]):
     72         ##  数据不完整
     73         return render(request,'register.html',{'errmsg':'数据不完整'})
     74     if not re.match('^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$',email):
     75         return render(request,'register.html',{'errmsg':'邮箱格式不正确'})
     76     if allow != 'on':
     77         return render(request,'register.html',{'errmsg':'请同意协议'})
     78 
     79     ##  3、保存进数据库
     80     user = User.objects.create_user(username,email,password)
     81     user.is_active = 0
     82     user.save()
     83 
     84     ##  4、返回应答
     85     return redirect('goods:index')
     86 '''
     87 
     88 class RegisterView(View):
     89     '''注册类视图'''
     90     def get(self,request):
     91         '''注册页面'''
     92         return render(request,'register.html')
     93     def post(self,request):
     94         '''注册处理'''
     95         ##  1、接收数据
     96         username = request.POST.get('user_name')
     97         password = request.POST.get('pwd')
     98         email = request.POST.get('email')
     99         allow = request.POST.get('allow')
    100 
    101         ##  校验username在数据库中是否存在
    102         try:
    103             user = User.objects.get(username=username)
    104         except Exception as e:
    105             user = None
    106         if user:
    107             ##  用户名存在
    108             return render(request, 'register.html', {'errmsg': '用户已存在'})
    109 
    110         ##  2、校验数据
    111         if not all([username, password, email]):
    112             ##  数据不完整
    113             return render(request, 'register.html', {'errmsg': '数据不完整'})
    114         if not re.match('^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$', email):
    115             return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
    116         if allow != 'on':
    117             return render(request, 'register.html', {'errmsg': '请同意协议'})
    118 
    119         ##  3、保存进数据库
    120         user = User.objects.create_user(username, email, password)
    121         user.is_active = 0
    122         user.save()
    123 
    124         ##  4、发送邮件验证 http://127.0.0.1:8000/user/register/active/id
    125         ##  对id 进行加密
    126         serializer = Serializer(settings.SECRET_KEY,3600)
    127         info = {'confirm':user.id}
    128         token = serializer.dumps(info).decode('utf8')
    129 
    130         ##  调用celery队列
    131         send_register_active_email.delay(email, username, token)
    132 
    133         ##  5、返回应答
    134         return redirect(reverse('goods:index'))
    135 
    136 class ActiveView(View):
    137     '''邮件认证处理'''
    138     def get(self,request,token):
    139         try:
    140             serializer = Serializer(settings.SECRET_KEY, 3600)
    141             res = serializer.loads(token)
    142 
    143             ##  获取用户名id
    144             user_id = res['confirm']
    145 
    146             ##  修改数据库
    147             user = User.objects.get(id=user_id)
    148             user.is_active = 1
    149             user.save()
    150 
    151             ##  跳转到首页
    152             return redirect(reverse('user:login'))
    153         except SignatureExpired as e:
    154             return HttpResponse('验证过期')
    155 
    156 class LoginView(View):
    157     '''登录页面'''
    158     def get(self,request):
    159         return render(request,'login.html')
    注册视图函数
    • (3)url配置

     1 from django.conf.urls import url
     2 from user.views import RegisterView,ActiveView,LoginView
     3 
     4 urlpatterns = [
     5     # url(r'^register$',views.register,name='register'),##    显示注册页面
     6     # url(r'^register_handle$',views.register_handle,name='register_handle'),##   注册处理
     7 
     8     url(r'^register$',RegisterView.as_view(),name='register'),##    注册
     9     url(r'^active/(?P<token>.*)',ActiveView.as_view(),name='active'),##  验证处理
    10     url(r'^login$',LoginView.as_view(),name='login'),## 登录页面
    11 ]
    url配置
    • (4)发送邮件的步骤

    发送邮件需要使用SMTP服务器,常用的免费服务器有:163126QQ,下面以163邮件为例。

    • 1)注册163邮箱itcast88,登录后设置。

    • 2)在新页面中勾选“开启”,弹出新窗口扫码发送短信,记住授权密码。

     

    • 3)打开dailyfresh/settings.py文件,进行下面配置配置。
     1 # 发送邮件配置
     2 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
     3 # smpt服务地址
     4 EMAIL_HOST = 'smtp.163.com'
     5 EMAIL_PORT = 25
     6 # 发送邮件的邮箱
     7 EMAIL_HOST_USER = 'a1240499170@163.com'
     8 # 在邮箱中设置的客户端授权密码
     9 EMAIL_HOST_PASSWORD = 'FDMQZVZOAILJZUVZ'
    10 # 收件人看到的发件人
    11 EMAIL_FROM = '天天生鲜<a1240499170@163.com>'
    settings配置
    • 4)视图函数中发送邮件
     1 from django.core.mail import send_mail
     2 def send_register_active_email(to_email, username, token):
     3     '''发送邮件'''
     4     ##  发送邮件
     5     subject = '天天生鲜欢迎信息'
     6     message = ''
     7     sender = settings.EMAIL_FROM
     8     reciever = [to_email]
     9     html_message = '''
    10                 <h1>%s, 欢迎您成为天天生鲜注册会员</h1>
    11                 <h3>请点击下面链接激活您的账户</h3>
    12                 <a href="http://127.0.0.1:8000/user/active/%s">
    13                     http://127.0.0.1:8000/user/active/%s
    14                 </a>
    15             ''' % (username, token, token)
    16     time.sleep(5)
    17     send_mail(subject, message, sender, reciever, html_message=html_message)
    视图函数
    • (5)调用celery异步队列的步骤

    详情见https://www.cnblogs.com/maoxinjueluo/p/12857651.html

    2、用户登录                                                                                                                                                                                                       

    • (1)用户注册的form表单
    <form method="post">
                            {% csrf_token %}
                            <input type="text" name="username" value="{{ username }}" class="name_input" 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" {{ checked }}>
                                <label>记住用户名</label>
                                <a href="#">忘记密码</a>
                            </div>
                            <input type="submit" name="" value="登录" class="input_submit">
                        </form>
    用户注册form表单
    • (2)对应视图函数

     1 from django.contrib.auth import authenticate,login
     2 class LoginView(View):
     3     '''登录页面'''
     4     def get(self,request):
     5         ##  判断是否记住密码
     6         if 'username' in request.COOKIES:
     7             username = request.COOKIES['username']
     8             checked = 'checked'
     9         else:
    10             username = ''
    11             checked = ''
    12         return render(request,'login.html',{'username':username,'checked':checked})
    13 
    14     def post(self,request):
    15         '''登录校验'''
    16         ##  1、接收数据
    17         username = request.POST.get('username')
    18         password = request.POST.get('pwd')
    19 
    20         ##  2、数据校验
    21         if not all([username,password]):
    22             ##  数据不完整
    23             return render(request,'login.html',{'errmsg':'用户名或密码不完整'})
    24 
    25         ##  3、业务处理
    26         ##  校验数据库
    27         user = authenticate(username=username, password=password)
    28         print(user)
    29         if user is not  None:
    30             ##  用户存在
    31             if user.is_active:
    32                 ##  用户已激活
    33                 login(request,user)
    34                 response = redirect(reverse('goods:index'))
    35 
    36                 ##  判断是否记住用户名
    37                 remember = request.POST.get('remember')
    38                 if remember == 'on':
    39                     ##  记住用户名
    40                     response.set_cookie('username',username,max_age=7*24*3600)
    41                 else:
    42                     response.delete_cookie('username')
    43                 ##  返回应答
    44                 return response
    45             else:
    46                 ##  用户未激活
    47                 return render(request,'login.html',{'errmsg':'用户未激活'})
    48 
    49         else:
    50             ##  用户名或密码错误
    51             return render(request,'login.html',{'errmsg':'用户名或密码错误'})
    登录视图函数
    • 补充:

    在登录时,如果登录的账号还未激活,可能会出现数据库校验一直未None的情况,使得用户登录信息判断失误

    解决方法:

    在django的setting文件中添加

    AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
    • (3)用Redis设置session缓存

    1)安装django-redis

    pip install diango-redis

     2)进行settings文件的配置

     1 # Django的缓存配置
     2 CACHES = {
     3     "default": {
     4         "BACKEND": "django_redis.cache.RedisCache",
     5         "LOCATION": "redis://127.0.0.1:6379/4",
     6         "OPTIONS": {
     7             "CLIENT_CLASS": "django_redis.client.DefaultClient",
     8         }
     9     }
    10 }
    11 # 配置session存储
    12 SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    13 SESSION_CACHE_ALIAS = "default"
    settings配置

    3、父模板抽象                                                                                                                                                                                                   

    将各个模板中共同有的模块抽取出来,抽象成父模板,子模板只需要继承父模板,改写父模板中不同的模块就可以了

    {# 首页 注册 登录 #}
    <!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">
    {% load staticfiles %}
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        {# 网页标题内容块 #}
        <title>{% block title %}{% endblock title %}</title>
        <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
        <link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
        {# 网页顶部引入文件块 #}
        {% block topfiles %}{% endblock topfiles %}
    </head>
    <body>
    {# 网页顶部欢迎信息块 #}
    {% block header_con %}
        <div class="header_con">
            <div class="header">
                <div class="welcome fl">欢迎来到天天生鲜!</div>
                <div class="fr">
                    {% if user.is_authenticated %}
                    <div class="login_btn fl">
                        欢迎您:<em>{{ user.username }}</em>
                        <span>|</span>
                        <a href="{% url 'user:logout' %}">退出</a>
                    </div>
                    {% else %}
                    <div class="login_btn fl">
                        <a href="{% url 'user:login' %}">登录</a>
                        <span>|</span>
                        <a href="{% url 'user:register' %}">注册</a>
                    </div>
                    {% endif %}
                    <div class="user_link fl">
                        <span>|</span>
                        <a href="{% url 'user:user' %}">用户中心</a>
                        <span>|</span>
                        <a href="cart.html">我的购物车</a>
                        <span>|</span>
                        <a href="{% url 'user:order' %}">我的订单</a>
                    </div>
                </div>
            </div>        
        </div>
    {% endblock header_con %}
    
    {# 网页顶部搜索框块 #}
    {% block search_bar %}
        <div class="search_bar clearfix">
            <a href="index.html" class="logo fl"><img src="images/logo.png"></a>
            <div class="search_con fl">
                <input type="text" class="input_text fl" name="" placeholder="搜索商品">
                <input type="button" class="input_btn fr" name="" value="搜索">
            </div>
            <div class="guest_cart fr">
                <a href="#" class="cart_name fl">我的购物车</a>
                <div class="goods_count fl" id="show_count">1</div>
            </div>
        </div>
    {% endblock search_bar %}
    
    {# 网站主体内容块 #}
    {% block body %}{% endblock body %}
    
        <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 北京天天生鲜信息技术有限公司 All Rights Reserved</p>
            <p>电话:010-****888    京ICP备*******8号</p>
        </div>
        {# 网页底部html元素块 #}
        {% block bottom %}{% endblock bottom %}
        {# 网页底部引入文件块 #}
        {% block bottomfiles %}{% endblock bottomfiles %}
    </body>
    </html>
    base.html
    {# 详情页 列表页 #}
    {% extends 'base.html' %}
    {# 网站主体内容块 #}
    {% block body %}
        <div class="navbar_con">
            <div class="navbar clearfix">
                <div class="subnav_con fl">
                    <h1>全部商品分类</h1>
                    <span></span>
                    <ul class="subnav">
                        <li><a href="#" class="fruit">新鲜水果</a></li>
                        <li><a href="#" class="seafood">海鲜水产</a></li>
                        <li><a href="#" class="meet">猪牛羊肉</a></li>
                        <li><a href="#" class="egg">禽类蛋品</a></li>
                        <li><a href="#" class="vegetables">新鲜蔬菜</a></li>
                        <li><a href="#" class="ice">速冻食品</a></li>
                    </ul>
                </div>
                <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>
        {# 详情页,列表页主体内容块 #}
        {% block main_content %}{% endblock main_content %}
    {% endblock body %}
    base_detail_list.html
    {# 购物车 提交订单 #}
    {% extends 'base.html' %}
    {% load staticfiles %}
    {# 网页顶部搜索框块 #}
    {% block search_bar %}
        <div class="search_bar clearfix">
            <a href="index.html" class="logo fl"><img src="{% static 'images/logo.png' %}"></a>
            <div class="sub_page_name fl">|&nbsp;&nbsp;&nbsp;&nbsp;{% block page_title %}{% endblock page_title %}</div>
            <div class="search_con fr">
                <input type="text" class="input_text fl" name="" placeholder="搜索商品">
                <input type="button" class="input_btn fr" name="" value="搜索">
            </div>
        </div>
    {% endblock search_bar %}
    base_no_cart.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 %}
    base_user_center.html

    4、用户中心                                                                                                                                                                                                       

    • 1)显示用户中心的视图函数及url配置

     1 class UserInfoView(View):
     2     '''用户中心-详情页'''
     3     def get(self,request):
     4         '''显示用户详情页'''
     5         return render(request,'user_center_info.html',{'page':'user'})
     6 
     7 class OrderView(View):
     8     '''用户中心-订单页'''
     9     def get(self,request):
    10         '''显示用户订单页'''
    11         return render(request,'user_center_order.html',{'page':'order'})
    12 
    13 class AddressView(View):
    14     '''用户中心-地址页'''
    15     def get(self,request):
    16         '''显示用户地址页'''
    17         return render(request,'user_center_site.html',{'page':'address'})
    用户中心视图函数
    1 urlpatterns = [
    2 
    3     url(r'^$',UserInfoView.as_view(),name='user'),##    用户中心-详情页
    4     url(r'^order$',OrderView.as_view(),name='order'),##     订单页
    5     url(r'^address$',AddressView.as_view(),name='address'),##   地址页
    6 
    7 ]
    对应的url文件

    出现问题:用户即使没有登录也可以访问用户中心,我们需要借用django内置的login_required装饰器对用户中心的访问进行限制,没有进行登录时,访问用户中心则跳转到用户登录页面。

    • 2)django内置的login_required装饰器

    由于需要在访问用户中心前判断用户是否已登录,所以需要在url文件中使用 login_required装饰器

    • 第一步,重新使用url文件
    url文件
    •  第二步,setting中添加设置LOGIN_URL
    作用:当判断用户未登录时,会跳转至LOGIN_URL指定的路径,此时还会在跳转路径后添加用户访问的url,如:/user/login/?next=/user/info/,当用户登录后会自动跳转至/user/info/下。
    
    LOGIN_URL = '/user/login/'
    • 第三步,登录视图函数接收 request.GET.get("next")
     1 class LoginView(View):
     2     '''登录页面'''
     3     def get(self,request):
     4         ##  判断是否记住密码
     5         if 'username' in request.COOKIES:
     6             username = request.COOKIES['username']
     7             checked = 'checked'
     8         else:
     9             username = ''
    10             checked = ''
    11         return render(request,'login.html',{'username':username,'checked':checked})
    12 
    13     def post(self,request):
    14         '''登录校验'''
    15         ##  1、接收数据
    16         username = request.POST.get('username')
    17         password = request.POST.get('pwd')
    18 
    19         ##  2、数据校验
    20         if not all([username,password]):
    21             ##  数据不完整
    22             return render(request,'login.html',{'errmsg':'用户名或密码不完整'})
    23 
    24         ##  3、业务处理
    25         ##  校验数据库
    26         user = authenticate(username=username, password=password)
    27         print(user)
    28         if user is not  None:
    29             ##  用户存在
    30             if user.is_active:
    31                 ##  用户已激活
    32 
    33                 ##  接收next
    34                 ##  指定默认值为'index'
    35                 ##  登录成功后跳转到next_url
    36                 next_url = request.GET.get('next',reverse('goods:index'))
    37                 login(request,user)
    38                 response = redirect(next_url)
    39 
    40                 ##  判断是否记住用户名
    41                 remember = request.POST.get('remember')
    42                 if remember == 'on':
    43                     ##  记住用户名
    44                     response.set_cookie('username',username,max_age=7*24*3600)
    45                 else:
    46                     response.delete_cookie('username')
    47                 ##  返回应答
    48                 return response
    49             else:
    50                 ##  用户未激活
    51                 return render(request,'login.html',{'errmsg':'用户未激活'})
    52 
    53         else:
    54             ##  用户名或密码错误
    55             return render(request,'login.html',{'errmsg':'用户名或密码错误'})
    登录的视图函数
    • 补充:

    我们会发现,其实上面的url配置还是很麻烦,只要是需要判断是否已登录的我们都需要调用 login_required 方法来封装as_view方法,所以我们可以创建一个文件封装好 login_required方法,然后继承这个文件就好了。下面我们创建这个文件

    1 from django.contrib.auth.decorators import login_required
    2 from django.views.generic import View
    3 
    4 class LoginRequiredMiXin(View):
    5     @classmethod
    6     def as_view(cls, **initkwargs):
    7         as_view = super().as_view(**initkwargs)
    8         return login_required(as_view)
    mixin.py

    我们在视图函数中直接继承mixin.py 中的 LoginRequireMiXin 方法就可以了

     1 from utils.mixin import LoginRequiredMiXin
     2 
     3 class UserInfoView(LoginRequiredMiXin,View):
     4     '''用户中心-详情页'''
     5     def get(self,request):
     6         '''显示用户详情页'''
     7         return render(request,'user_center_info.html',{'page':'user'})
     8 
     9 class OrderView(LoginRequiredMiXin,View):
    10     '''用户中心-订单页'''
    11     def get(self,request):
    12         '''显示用户订单页'''
    13         return render(request,'user_center_order.html',{'page':'order'})
    14 
    15 class AddressView(LoginRequiredMiXin,View):
    16     '''用户中心-地址页'''
    17     def get(self,request):
    18         '''显示用户地址页'''
    19         return render(request,'user_center_site.html',{'page':'address'})
    视图函数
    • 3)登录判断、登录和退出小结

    from django.contrib.auth import authenticate,login,logout
    
    ##    判断数据库中是否存在此用户
    authenticate(username,password)        #    存在返回True,反之返回False
    
    
    ##    记录用户的登录状态
    login(request,user)
    
    
    ##    退出登录
    logout(request)
    • 4)用户中心------地址页面

    模板文件

    {% 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" name="" value="提交" class="info_submit">
                        </form>
                    </div>
            </div>
    {% endblock right_content %}
    地址的模板文件

    视图函数

     1 class AddressView(LoginRequiredMiXin,View):
     2     '''用户中心-地址页'''
     3     def get(self,request):
     4         '''显示用户地址页'''
     5         ##  显示数据
     6         ##  判断是否为默认收货地址
     7         user = request.user
     8         try:
     9             address = Address.objects.get(user=user, is_default=True)
    10         except Address.DoesNotExist:
    11             address = None
    12 
    13         return render(request,'user_center_site.html',{'page':'address','address':address})
    14 
    15     def post(self,request):
    16         '''接收地址数据'''
    17         ##  接收数据
    18         receiver = request.POST.get('receiver')
    19         addr = request.POST.get('addr')
    20         zip_code = request.POST.get('zip_code')
    21         phone = request.POST.get('phone')
    22 
    23         ##  数据校验
    24         ##  校验数据是否完整
    25         if not all([receiver,addr,phone]):
    26             return render(request,'user_center_site.html',{'errmsg':'数据不完整'})
    27         ##  校验手机是否合法
    28         if not re.match(r'^1[3|4|5|7|8][0-9]{9}$',phone):
    29             return render(request,'user_center_site.html',{'errmsg':'手机格式不正确'})
    30 
    31         ##  业务处理
    32         ##  判断是否为默认收货地址
    33         user = request.user
    34         try:
    35             address = Address.objects.get(user=user,is_default=True)
    36         except Address.DoesNotExist:
    37             address = None
    38         if address:
    39             ##  已有默认收货地址
    40             is_default = False
    41         else:
    42             is_default = True
    43 
    44         ##  保存数据
    45         Address.objects.create(user=user,
    46                        receiver=receiver,
    47                        addr=addr,zip_code=zip_code,
    48                        phone=phone,
    49                        is_default=is_default)
    50 
    51         ##  返回应答
    52         return redirect('user:address')
    地址视图函数

    上面的视图函数中可以发现get函数和post函数中都需要判断是否为默认收货地址,我们可以将这一段代码封装在模型类中,在视图函数中直接调用模型类中的方法就可以了

     1 class AddressManager(models.Manager):
     2     '''地址模型管理器类'''
     3     def get_default_address(self,user):
     4         try:
     5             address = self.get(user=user, is_default=True)
     6         except self.model.DoesNotExist:
     7             address = None
     8 
     9         return address
    10 
    11 class Address(BaseModel):
    12     '''地址模型类'''
    13     
    14     object = AddressManager()
    模型类
     1 class AddressView(LoginRequiredMiXin,View):
     2     '''用户中心-地址页'''
     3     def get(self,request):
     4         '''显示用户地址页'''
     5         ##  显示数据
     6         ##  判断是否为默认收货地址
     7         user = request.user
     8         # try:
     9         #     address = Address.objects.get(user=user, is_default=True)
    10         # except Address.DoesNotExist:
    11         #     address = None
    12 
    13         ##  调用模型类中封装的方法判断是否为默认收货地址
    14         address = Address.objects.get_default_address(user)
    15 
    16         return render(request,'user_center_site.html',{'page':'address','address':address})
    17 
    18     def post(self,request):
    19         '''接收地址数据'''
    20         ##  接收数据
    21         receiver = request.POST.get('receiver')
    22         addr = request.POST.get('addr')
    23         zip_code = request.POST.get('zip_code')
    24         phone = request.POST.get('phone')
    25 
    26         ##  数据校验
    27         ##  校验数据是否完整
    28         if not all([receiver,addr,phone]):
    29             return render(request,'user_center_site.html',{'errmsg':'数据不完整'})
    30         ##  校验手机是否合法
    31         if not re.match(r'^1[3|4|5|7|8][0-9]{9}$',phone):
    32             return render(request,'user_center_site.html',{'errmsg':'手机格式不正确'})
    33 
    34         ##  业务处理
    35         ##  判断是否为默认收货地址
    36         user = request.user
    37         # try:
    38         #     address = Address.objects.get(user=user,is_default=True)
    39         # except Address.DoesNotExist:
    40         #     address = None
    41 
    42         ##  调用模型类中封装的方法判断是否为默认收货地址
    43         address = Address.objects.get_default_address(user)
    44 
    45         if address:
    46             ##  已有默认收货地址
    47             is_default = False
    48         else:
    49             is_default = True
    50 
    51         ##  保存数据
    52         Address.objects.create(user=user,
    53                        receiver=receiver,
    54                        addr=addr,zip_code=zip_code,
    55                        phone=phone,
    56                        is_default=is_default)
    57 
    58         ##  返回应答
    59         return redirect('user:address')
    修改后的视图函数
    • 5)用户中心--------详情页

    获取用户的历史浏览记录,由于历史浏览记录对于数据库的交互次数是很多的,所以这里使用Redis保存历史浏览记录

    有两种方法可以进行Python与Redis的交互

    • 第一种
    1 from redis import StrictRedis
    2 
    3 sr = StrictRedis(host='127.0.0.1',port=6379,db=4)  ##    这里如果用到的是本机的Redis,括号里面的参数可以不填
    4 
    5 数据库操作。。。
    • 第二种使用diango-redis提供的方法

    需要先配置settings文件

     1 # Django的缓存配置
     2 CACHES = {
     3     "default": {
     4         "BACKEND": "django_redis.cache.RedisCache",
     5         "LOCATION": "redis://127.0.0.1:6379/4",
     6         "OPTIONS": {
     7             "CLIENT_CLASS": "django_redis.client.DefaultClient",
     8         }
     9     }
    10 }
    settings配置
    from django-redis import get_redis_connection
    
    con = get_redis_connection        ##    效果和第一种方法是一样的,也是得到一个StrictRedis的实例对象
     1 class UserInfoView(LoginRequiredMiXin,View):
     2     '''用户中心-详情页'''
     3     def get(self,request):
     4         '''显示用户详情页'''
     5 
     6         ##  显示用户个人信息
     7         user = request.user
     8         address = Address.objects.get_default_address(user)
     9 
    10         ##  用Redis存储历史浏览记录,查询历史浏览记录
    11         # from redis import StrictRedis
    12         # sr = StrictRedis(host='127.0.0.1',port=6379,db=4)
    13 
    14         ##  用django自带的get_redis_connection也可以做到
    15         ##  得到一个StrictRedis对象
    16         con = get_redis_connection('default')
    17         ##  组织查询的key
    18         history_key = 'history_%s'%user.id
    19         ##  进行查询,得到查询集sku_id
    20         sku_ids = con.lrange(history_key,0,4)
    21 
    22         ##  向goods/models 中的GoodsSKU查询数据,遍历sku_ids 进行查询
    23         goods_list = []
    24         for goods_id in sku_ids:
    25             good = GoodsSKU.objects.get(id=goods_id)
    26             goods_list.append(good)
    27 
    28         ##  组织上下文
    29         context = {'page':'user',
    30                    'address':address,
    31                    'goods_list':goods_list}
    32 
    33         return render(request,'user_center_info.html',context)
    用户中心获取浏览记录的视图函数
    {% 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 good in goods_list %}
                                <li>
                                    <a href="detail.html"><img src="{{ good.image.url }}"></a>
                                    <h4><a href="detail.html">{{ good.name }}</a></h4>
                                    <div class="operate">
                                        <span class="prize">¥{{ good.price }}</span>
                                        <span class="unit">{{ good.price }}/{{ good.unite }}g</span>
                                        <a href="#" class="add_goods" title="加入购物车"></a>
                                    </div>
                                </li>
                            {% empty %}
                                <h1>无历史浏览记录</h1>
                            {% endfor %}
    
                </ul>
            </div>
            </div>
    {% endblock right_content %}
    用户中心的模板文件

    商品模块-------- goods

    1、首页内容获取以及Redis存储购物车数据分析                                                                                                                                                                      

    首页view函数

     1 from django.shortcuts import render
     2 from django.views.generic import View
     3 from django_redis import get_redis_connection
     4 from django.core.cache import cache
     5 from goods.models import GoodsType,IndexGoodsBanner,IndexPromotionBanner,IndexTypeGoodsBanner
     6 
     7 # Create your views here.
     8 
     9 class IndexView(View):
    10     '''首页'''
    11     def get(self,request):
    12         '''显示首页'''
    13 
    14         ##  获取缓存
    15         context = cache.get('index_page_data')
    16         if context is None:
    17             print('设置缓存')
    18 
    19             # 获取商品的种类信息
    20             types = GoodsType.objects.all()
    21 
    22             # 获取首页轮播商品信息
    23             goods_banners = IndexGoodsBanner.objects.all().order_by('index')
    24 
    25             # 获取首页促销活动信息
    26             promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
    27 
    28             # 获取首页分类商品展示信息
    29             for type in types:  # GoodsType
    30                 # 获取type种类首页分类商品的图片展示信息
    31                 image_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
    32                 # 获取type种类首页分类商品的文字展示信息
    33                 title_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0).order_by('index')
    34 
    35                 # 动态给type增加属性,分别保存首页分类商品的图片展示信息和文字展示信息
    36                 type.image_banners = image_banners
    37                 type.title_banners = title_banners
    38 
    39             context = {'types': types,
    40                        'goods_banners': goods_banners,
    41                        'promotion_banners': promotion_banners}
    42 
    43             ##  设置缓存
    44             cache.set('index_page_data', context, 3600)
    45 
    46         # 获取用户购物车中商品的数目
    47         user = request.user
    48         cart_count = 0
    49         if user.is_authenticated:
    50             # 用户已登录
    51             conn = get_redis_connection('default')
    52             cart_key = 'cart_%d' % user.id
    53             cart_count = conn.hlen(cart_key)
    54 
    55 
    56 
    57         # 组织模板上下文
    58         context.update(cart_count=cart_count)
    59 
    60 
    61         # 使用模板
    62         return render(request, 'index.html', context)
    首页view视图
    {% extends 'base.html' %}
    {% load static %}
    {% block title %}天天生鲜-首页{% endblock title %}
    {% block topfiles %}
        <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
        <script type="text/javascript" src="{% static 'js/jquery-ui.min.js' %}"></script>
        <script type="text/javascript" src="{% static 'js/slide.js' %}"></script>
    {% endblock topfiles %}
    {% block body %}
        <div class="navbar_con">
            <div class="navbar">
                <h1 class="fl">全部商品分类</h1>
                <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="center_con clearfix">
            <ul class="subnav fl">
                {% for type in types %}
                    <li><a href="#model0{{ forloop.counter }}" class="{{ type.logo }}">{{ type.name }}</a></li>
                {% endfor %}
            </ul>
            <div class="slide fl">
                <ul class="slide_pics">
                    {% for banner in goods_banners  %}
                        <li><a href="{% url 'goods:detail' banner.sku.id %}"><img src="{{ banner.image.url }}" alt="幻灯片"></a></li>
                    {% endfor %}
                </ul>
                <div class="prev"></div>
                <div class="next"></div>
                <ul class="points"></ul>
            </div>
            <div class="adv fl">
                {% for banner in promotion_banners %}
                    <a href="{{ banner.url }}"><img src="{{ banner.image.url }}"></a>
                {% endfor %}
            </div>
        </div>
    
        {% for type in types %}
        <div class="list_model">
            <div class="list_title clearfix">
                <h3 class="fl" id="model0{{ forloop.counter }}">{{ type.name }}</h3>
                <div class="subtitle fl">
                    <span>|</span>
                    {% for banner in type.title_banners %}
                        <a href="{% url 'goods:detail' banner.sku.id  %}">{{ banner.sku.name }}</a>
                    {% endfor %}
                </div>
                <a href="#" class="goods_more fr" id="fruit_more">查看更多 ></a>
            </div>
    
            <div class="goods_con clearfix">
                <div class="goods_banner fl"><img src="{{ type.image.url }}"></div>
                <ul class="goods_list fl">
                    {% for banner in type.image_banners %}
                    <li>
                        <h4><a href="{% url 'goods:detail' banner.sku.id  %}">{{ banner.sku.name }}</a></h4>
                        <a href="{% url 'goods:detail' banner.sku.id  %}"><img src="{{ banner.sku.image.url }}"></a>
                        <div class="prize">¥ {{ banner.sku.price }}</div>
                    </li>
                    {% endfor %}
                </ul>
            </div>
        </div>
        {% endfor %}
    {% endblock body %}
    模板文件

    这里Redis存储用户信息用的数据形式是hash

     参考文档:http://doc.redisfans.com/hash/hlen.html

    2、FastDFS和Nignx上传商品图片                                                                                                                                                                                     

    • 1)分布式文件系统FastDFS

    在本次项目中商品的图片将存储在分布式文件新系统FastDFS中,FastDFS暂时还不支持window系统,所以安装使用都是在Linux系统中,FastDFS的安装配置见https://www.cnblogs.com/maoxinjueluo/p/12842719.html

    • 2)项目中上传和使用图片的流程(重点)

     使用FastDFS和Nignx的好处:

    海量存储,存储容量扩展方便。

    文件内容重复。

    结合nginx提高网站访问图片的效率。

    • 3)自定义文件存储类

    因为django默认后台文件上传的文件会存储在本地电脑中,所以我们要自定义文件存储类,修改文件存储的路径,将文件存储到远程FastDFS分布式文件存储服务器中。

    (1)创建一个文件管理storage文件(storage编写自定义文件存储类)

     (2)改写client.conf的内容

    详情见https://www.cnblogs.com/maoxinjueluo/p/12842719.html 中的第6条,这里将client.conf存在在fdfs文件中。

    (3)编写storeage.py中的自定义文件存储类以及设置settings文件

    1 # 设置Django的文件存储类
    2 DEFAULT_FILE_STORAGE='utils.fdfs.storage.FDFSStorage'
    3 
    4 # 设置fdfs使用的client.conf文件路径
    5 FDFS_CLIENT_CONF='./utils/fdfs/client.conf'
    6 
    7 # 设置fdfs存储服务器上nginx的IP和端口号
    8 FDFS_URL='http://192.168.43.62:8888/'
    settings文件配置
     1 from django.core.files.storage import Storage
     2 from fdfs_client.client import Fdfs_client
     3 from django.conf import settings
     4 
     5 class FDFSStorage(Storage):
     6     '''fast dfs 文件存储类'''
     7     def __init__(self, client_conf=None, base_url=None):
     8         '''初始化'''
     9         if client_conf is None:
    10             client_conf = settings.FDFS_CLIENT_CONF
    11         self.client_conf = client_conf
    12 
    13         if base_url is None:
    14             base_url = settings.FDFS_URL
    15         self.base_url = base_url
    16 
    17     def _open(self, name, mode='rb'):
    18         '''打开文件时使用'''
    19         pass
    20 
    21     def _save(self, name, content):
    22         '''保存文件时使用'''
    23         ##  name:上传的文件名
    24         ##  content:包含上传文件内容的file对象
    25 
    26         ##  创建一个client对象
    27         client = Fdfs_client(self.client_conf)
    28 
    29         ##  上传文件到fast dfs系统中
    30         res = client.upload_by_buffer(content.read())
    31 
    32         # dict
    33         # {
    34         #     'Group name': group_name,
    35         #     'Remote file_id': remote_file_id,
    36         #     'Status': 'Upload successed.',
    37         #     'Local file name': '',
    38         #     'Uploaded size': upload_size,
    39         #     'Storage IP': storage_ip
    40         # }
    41         if res.get('Status') != 'Upload successed.':
    42             # 上传失败
    43             raise Exception('上传文件到fast dfs失败')
    44 
    45         # 获取返回的文件ID
    46         filename = res.get('Remote file_id')
    47 
    48         return filename
    49 
    50     def exists(self, name):
    51         '''Django判断文件名是否可用'''
    52         return False
    53 
    54     def url(self,name):
    55         '''返回访问文件的url路径'''
    56         return self.base_url+name
    storage.py中的自定义文件存储类

    fdfs上传图片小结:

    3、首页                                                                                                                                                                                                                                           

    • 1)celery生成首页静态页面

    由于首页无论是否登录都可以访问,并且首页是相对不变的,所以可以将首页生成静态页面,减少数据库的访问,当管理员登录后台管理页面修改商品时再重新生成静态页面,可以提高首页的访问速度。

    详情见 https://www.cnblogs.com/maoxinjueluo/p/12857651.html 第3条。

    • 2)admin管理更新首页数据表数据时重新生成index静态页面

     详情见 https://www.cnblogs.com/maoxinjueluo/p/12857651.html 第4条。

    • 3)首页数据缓存设置和获取

     1 from django.shortcuts import render
     2 from django.views.generic import View
     3 from django_redis import get_redis_connection
     4 from django.core.cache import cache
     5 from goods.models import GoodsType,IndexGoodsBanner,IndexPromotionBanner,IndexTypeGoodsBanner
     6 
     7 # Create your views here.
     8 
     9 class IndexView(View):
    10     '''首页'''
    11     def get(self,request):
    12         '''显示首页'''
    13 
    14         ##  获取缓存
    15         context = cache.get('index_page_data')
    16         if context is None:
    17             print('设置缓存')
    18 
    19             # 获取商品的种类信息
    20             types = GoodsType.objects.all()
    21 
    22             # 获取首页轮播商品信息
    23             goods_banners = IndexGoodsBanner.objects.all().order_by('index')
    24 
    25             # 获取首页促销活动信息
    26             promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
    27 
    28             # 获取首页分类商品展示信息
    29             for type in types:  # GoodsType
    30                 # 获取type种类首页分类商品的图片展示信息
    31                 image_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
    32                 # 获取type种类首页分类商品的文字展示信息
    33                 title_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0).order_by('index')
    34 
    35                 # 动态给type增加属性,分别保存首页分类商品的图片展示信息和文字展示信息
    36                 type.image_banners = image_banners
    37                 type.title_banners = title_banners
    38 
    39             context = {'types': types,
    40                        'goods_banners': goods_banners,
    41                        'promotion_banners': promotion_banners}
    42 
    43             ##  设置缓存
    44             cache.set('index_page_data', context, 3600)
    45 
    46         # 获取用户购物车中商品的数目
    47         user = request.user
    48         cart_count = 0
    49         if user.is_authenticated:
    50             # 用户已登录
    51             conn = get_redis_connection('default')
    52             cart_key = 'cart_%d' % user.id
    53             cart_count = conn.hlen(cart_key)
    54 
    55 
    56 
    57         # 组织模板上下文
    58         context.update(cart_count=cart_count)
    59 
    60 
    61         # 使用模板
    62         return render(request, 'index.html', context)
    63 
    64 class DetailView(View):
    65     '''详情页'''
    66     def get(self,request,a):
    67         '''显示详情页'''
    68         return render(request,'detail.html')
    商品的视图函数

    当管理员修改后台商品时,应该要删除缓存

     1 from django.contrib import admin
     2 from django.core.cache import cache
     3 from goods.models import GoodsType,GoodsSKU,Goods,GoodsImage,IndexGoodsBanner,IndexTypeGoodsBanner,IndexPromotionBanner
     4 
     5 # Register your models here.
     6 
     7 class BaseModelAdmin(admin.ModelAdmin):
     8     '''更新或删除生成首页静态页面模型类'''
     9 
    10     def save_model(self, request, obj, form, change):
    11         '''更新时调用'''
    12         super().save_model(request,obj,form,change)
    13         ##  发送任务队列
    14         from celery_tasks.tasks import generate_static_index_html
    15         generate_static_index_html.delay()
    16 
    17         ##  删除缓存
    18         cache.delete('index_page_data')
    19 
    20 
    21 
    22     def delete_model(self, request, obj):
    23         '''删除时调用'''
    24         super().delete_model(request, obj)
    25         ##  发送任务队列
    26         from celery_tasks.tasks import generate_static_index_html
    27         generate_static_index_html.delay()
    28 
    29         ##  删除缓存
    30         cache.delete('index_page_data')
    goods中的admin.py

    参考文档:https://yiyibooks.cn/xx/django_182/topics/cache.html

    • 4)页面静态化和缓存数据小结:

    4、商品详情页                                                                                                                                                                                                                   

    视图函数:

     1 ##      /goods/detailid
     2 class DetailView(View):
     3     '''详情页'''
     4     def get(self,request,goods_id):
     5         '''显示详情页'''
     6         ##  1、查询数据
     7         ##  查询商品
     8         try:
     9             ##  商品存在
    10             sku = GoodsSKU.objects.get(id=goods_id)
    11         except Exception:
    12             return redirect(reverse('goods:index'))
    13 
    14         ##  查询商品种类
    15         types = GoodsType.objects.all()
    16 
    17         ##  商品评论
    18         goods_comments = OrderGoods.objects.filter(sku=sku).order_by('create_time').exclude(comment='')
    19 
    20         ##  新品推荐
    21         new_skus = GoodsSKU.objects.filter(type=sku.type).exclude(id=sku.id).order_by('-create_time')[:2]
    22 
    23         ##  查询同一个SPU下的不同商品
    24         same_spu_skus = GoodsSKU.objects.filter(goods=sku.goods).exclude(id=sku.id)
    25 
    26         ##  获取购物车数量
    27         user = request.user
    28         cart_count = 0
    29         if user.is_authenticated:
    30             ##  用户已登录
    31             conn = get_redis_connection('default')      ##  连接Redis
    32             cart_key = 'cart_%d'%user.id
    33             cart_count = conn.hlen(cart_key)
    34 
    35             ##  添加历史浏览记录
    36             history_id = 'history_%d'%user.id
    37             conn = get_redis_connection('default')
    38             ##  删除列表中的history_id
    39             conn.lrem(history_id,0,sku.id)
    40             ##  从左侧插入
    41             conn.lpush(history_id,sku.id)
    42             ##  只保留5条记录
    43             conn.ltrim(history_id,0,4)
    44 
    45 
    46         ##  2、组织模板上下文
    47         context = {
    48             'sku':sku,
    49             'types':types,
    50             'goods_comments':goods_comments,
    51             'new_skus':new_skus,
    52             'same_spu_skus':same_spu_skus,
    53             'cart_count':cart_count
    54         }
    55 
    56         ##  3、返回应答
    57         return render(request,'detail.html',context)
    视图函数

    模板文件:

    {% extends 'base_detail_list.html' %}
    {% load static %}
    {% block title %}天天生鲜-商品详情{% endblock %}
    
    
    {% block main_content %}
        <div class="breadcrumb">
            <a href="#">全部分类</a>
            <span>></span>
            <a href="#">{{ sku.type.name }}</a>
            <span>></span>
            <a href="#">商品详情</a>
        </div>
    
        <div class="goods_detail_con clearfix">
            <div class="goods_detail_pic fl"><img src="{{ sku.image.url }}"></div>
    
            <div class="goods_detail_list fr">
                <h3>{{ sku.name }}</h3>
                <p>{{ sku.desc }}</p>
                <div class="prize_bar">
                    <span class="show_pirze">¥<em>{{ sku.price }}</em></span>
                    <span class="show_unit">单  位:{{ sku.unite }}</span>
                </div>
                <div class="goods_num clearfix">
                    <div class="num_name fl">数 量:</div>
                    <div class="num_add fl">
                        <input type="text" class="num_show fl" value="1">
                        <a href="javascript:;" class="add fr">+</a>
                        <a href="javascript:;" class="minus fr">-</a>
                    </div>
                </div>
                <div class="total">总价:<em>16.80元</em></div>
                <div class="operate_btn">
                    <a href="javascript:;" class="buy_btn">立即购买</a>
                    <a href="javascript:;" class="add_cart" id="add_cart">加入购物车</a>                
                </div>
                <div>
                    <p>其它规格:</p>
                    <ul>
                        {% for sku in same_spu_skus %}
                            <li><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
        </div>
    
        <div class="main_wrap clearfix">
            <div class="l_wrap fl clearfix">
                <div class="new_goods">
                    <h3>新品推荐</h3>
                    <ul>
                        {% for new_sku in new_skus %}
                            <li>
                                <a href="{% url 'goods:detail' new_sku.id %}"><img src="{{ new_sku.image.url }}"></a>
                                <h4><a href="{% url 'goods:detail' new_sku.id %}">{{ new_sku.name }}</a></h4>
                                <div class="prize">¥{{ new_sku.price }}</div>
                            </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
    
            <div class="r_wrap fr clearfix">
                <ul class="detail_tab clearfix">
                    <li class="active">商品介绍</li>
                    <li>评论</li>
                </ul>
    
                <div class="tab_content">
                    <dl>
                        <dt>商品详情:</dt>
                        <dd>{{ sku.goods.detail|safe }}</dd>
                    </dl>
                </div>
    
                <div class="tab_content">
                    <dl>
                        {% for order in sku_orders %}
                        <dt>评论时间:{{ order.update_time }}&nbsp;&nbsp;用户名:{{ order.order.user.username }}</dt>
                        <dd>评论内容:{{ order.comment }}</dd>
                        {% endfor %}
                    </dl>
                </div>
            </div>
        </div>
    {% endblock main_content %}
    
    {% block bottom %}    <div class="add_jump"></div>      {% endblock bottom %}
    {% block bottomfiles %}
        <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
        <script type="text/javascript">
            update_goods_amount()
            // 计算商品的总价格
            function update_goods_amount() {
                // 获取商品的单价和数量
                price = $('.show_pirze').children('em').text()
                count = $('.num_show').val()
                // 计算商品的总价
                price = parseFloat(price)
                count = parseInt(count)
                amount = price*count
                // 设置商品的总价
                $('.total').children('em').text(amount.toFixed(2)+'')
            }
    
            // 增加商品的数量
            $('.add').click(function () {
                // 获取商品原有的数目
                count = $('.num_show').val()
                // 加1
                count = parseInt(count)+1
                // 重新设置商品的数目
                $('.num_show').val(count)
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 减少商品的数量
            $('.minus').click(function () {
                // 获取商品原有的数目
                count = $('.num_show').val()
                // 减1
                count = parseInt(count)-1
                if (count <= 0){
                    count = 1
                }
                // 重新设置商品的数目
                $('.num_show').val(count)
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 手动输入商品的数量
            $('.num_show').blur(function () {
                // 获取用户输入的数目
                count = $(this).val()
                // 校验count是否合法
                if (isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
                    count = 1
                }
                // 重新设置商品的数目
                $(this).val(parseInt(count))
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 获取add_cart div元素左上角的坐标
            var $add_x = $('#add_cart').offset().top;
            var $add_y = $('#add_cart').offset().left;
    
            // 获取show_count div元素左上角的坐标
            var $to_x = $('#show_count').offset().top;
            var $to_y = $('#show_count').offset().left;
    
    
            $('#add_cart').click(function(){
                // 获取商品id和商品数量
                sku_id = $(this).attr('sku_id') // attr prop
                count = $('.num_show').val()
                csrf = $('input[name="csrfmiddlewaretoken"]').val()
                // 组织参数
                params = {'sku_id':sku_id, 'count':count, 'csrfmiddlewaretoken':csrf}
                // 发起ajax post请求,访问/cart/add, 传递参数:sku_id count
                $.post('/cart/add', params, function (data) {
                    if (data.res == 5){
                        // 添加成功
                        $(".add_jump").css({'left':$add_y+80,'top':$add_x+10,'display':'block'})
                        $(".add_jump").stop().animate({
                            'left': $to_y+7,
                            'top': $to_x+7},
                            "fast", function() {
                                $(".add_jump").fadeOut('fast',function(){
                                    // 重新设置用户购物车中商品的条目数
                                    $('#show_count').html(data.total_count);
                                });
                        });
                    }
                    else{
                        // 添加失败
                        alert(data.errmsg)
                    }
                })
            })
        </script>
    {% endblock bottomfiles %}
    </body>
    </html>
    详情页的模板文件

    5、商品列表页                                                                                                                                                                                                                            

    视图函数:

     1 ##      /goods/list/type/page?sort=1
     2 class ListView(View):
     3     '''商品列表页'''
     4     def get(self,request,type_id,page):
     5         '''显示商品列表'''
     6 
     7         ##  获取商品类型
     8         try:
     9             type = GoodsType.objects.get(id=type_id)
    10         except GoodsType.DoesNotExist:
    11             ##  种类不存在
    12             return redirect(reverse('goods:index'))
    13 
    14         ##  获取商品的分类信息
    15         types = GoodsType.objects.all()
    16 
    17         ##  获取排序方式
    18         sort = request.GET.get('sort')
    19         if sort == 'price':
    20             ##  获取种类下的所有商品信息
    21             goods_skus = GoodsSKU.objects.filter(type=type).order_by('price')
    22         elif sort == 'hot':
    23             goods_skus = GoodsSKU.objects.filter(type=type).order_by('-sales')
    24         else:
    25             goods_skus = GoodsSKU.objects.filter(type=type).order_by('-id')
    26             sort = 'default'
    27 
    28 
    29         ##  获取所有商品的页码
    30         paginator = Paginator(goods_skus,1)
    31         #   获取传过来的页码,并且校验
    32         try:
    33             page = int(page)
    34         except Exception:
    35             page = 1
    36         if page > paginator.num_pages:
    37             page = 1
    38 
    39         ##  页面上最多显示5页页码
    40         ##  总页数小于5,显示全部页数
    41         ##  当前页小于等于3,显示1-5页
    42         ##  当前页大于等于倒数第3,显示后5页
    43         ##  其它情况,显示当前页的前两页,当前页,后两页
    44         if paginator.num_pages < 6:
    45             pages = paginator.page_range
    46         elif page <= 3:
    47             pages = range(1,6)
    48         elif (paginator.num_pages-page) <= 2:
    49             pages = range(paginator.num_pages-4,paginator.num_pages+1)
    50         else:
    51             pages = range(page-2,page+3)
    52 
    53         ##  获取page页的page实例对象
    54         skus_page = paginator.page(page)
    55 
    56         ##  新品推荐
    57         new_skus = GoodsSKU.objects.filter(type=type).order_by('-create_time')[:2]
    58 
    59         # 获取用户购物车中商品的数目
    60         user = request.user
    61         cart_count = 0
    62         if user.is_authenticated:
    63             # 用户已登录
    64             conn = get_redis_connection('default')
    65             cart_key = 'cart_%d' % user.id
    66             cart_count = conn.hlen(cart_key)
    67 
    68         ##  组织模板上下文
    69         context = {
    70             'type':type,
    71             'types':types,
    72             'sort':sort,
    73             'paginator':paginator,
    74             'skus_page':skus_page,
    75             'new_skus':new_skus,
    76             'cart_count':cart_count,
    77             'pages':pages
    78         }
    79 
    80         return render(request,'list.html',context)
    商品列表的视图函数

    模板文件:

    {% extends 'base_detail_list.html' %}
    {% block title %}天天生鲜-商品列表{% endblock title %}
    
    {% block main_content %}
        <div class="breadcrumb">
            <a href="#">全部分类</a>
            <span>></span>
            <a href="{% url 'goods:list' type.id 1 %}?sort=default">{{ type.name }}</a>
            <span>></span>
            <a href="#">商品详情</a>
        </div>
    
        <div class="main_wrap clearfix">
            <div class="l_wrap fl clearfix">
                <div class="new_goods">
                    <h3>新品推荐</h3>
                    <ul>
                        {% for sku in new_skus %}
                            <li>
                                <a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
                                <h4><a href="{% url 'goods:detail' sku.id %}">{{sku.name}}</a></h4>
                                <div class="prize">¥{{ sku.price }}</div>
                            </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
    
            <div class="r_wrap fr clearfix">
                <div class="sort_bar">
                    <a href="{% url 'goods:list' type.id 1 %}" {% if sort == 'default' %}class="active"{% endif %}>默认</a>
                    <a href="{% url 'goods:list' type.id 1 %}?sort=price" {% if sort == 'price' %}class="active"{% endif %}>价格</a>
                    <a href="{% url 'goods:list' type.id 1 %}?sort=hot" {% if sort == 'hot' %}class="active"{% endif %}>人气</a>
                </div>
    
                <ul class="goods_type_list clearfix">
                    {% for sku in skus_page %}
                        <li>
                            <a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
                            <h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4>
                            <div class="operate">
                                <span class="prize">¥{{ sku.price }}</span>
                                <span class="unit">{{ sku.price }}/{{ sku.unite }}</span>
                                <a href="#" class="add_goods" title="加入购物车"></a>
                            </div>
                        </li>
                    {% endfor %}
                </ul>
    
                <div class="pagenation">
                    {% if skus_page.has_previous %}
                        <a href="#"><上一页</a>
                    {% endif %}
                    {% for pindex in pages %}
                        {% if pindex == skus_page.number %}
                            <a href="{% url 'goods:list' type.id pindex %}?sort={{sort}}" class="active">{{pindex}}</a>
                        {% else %}
                            <a href="{% url 'goods:list' type.id pindex %}?sort={{sort}}">{{pindex}}</a>
                        {% endif %}
                    {% endfor %}
                    {% if skus_page.has_next %}
                        <a href="#">下一页></a>
                    {% endif %}
                </div>
            </div>
        </div>
    {% endblock main_content %}
    模板文件

    其中页码控制的参考文档:https://yiyibooks.cn/xx/django_182/topics/pagination.html

    6、搜索框的设计                                                                                                                                                                                                                              

    搜索框的设计参考:

    全文检索框架haystack和搜索引擎whoosh的使用

    购物车模块------cart

    1、购物车记录的添加                                                                                                                                                                                                                              

    前端商品页面中购物车的添加,由于前端页面只需要刷新购物车的数量,不需要刷新整个页面,所以采用ajax请求向后端传递数据

    后端视图函数:

     1 from django.shortcuts import render
     2 from django.http import JsonResponse
     3 from django_redis import get_redis_connection
     4 from django.views.generic import View
     5 from goods.models import GoodsSKU
     6 from utils.mixin import LoginRequiredMiXin
     7 
     8 # Create your views here.
     9 
    10 class CartAddView(View):
    11     '''添加购物车'''
    12     def post(self,request):
    13         '''处理ajax请求'''
    14         ##  获取用户,判断用户是否登录,如果未登录则不能添加购物车
    15         user = request.user
    16         if not user.is_authenticated:
    17             ##  用户未登录
    18             return JsonResponse({'res':0,'errmsg':'用户未登录'})
    19 
    20         ##  1、接收数据  传过来的参数: sku_id  count
    21         sku_id = request.POST.get('sku_id')
    22         count = request.POST.get('count')
    23 
    24 
    25         ##  2、数据校验
    26         ##  校验数据的完整性
    27         if not all([sku_id,count]):
    28             return JsonResponse({'res':1,'errmsg':'数据不完整'})
    29         ##  校验商品是否存在
    30         try:
    31             sku = GoodsSKU.objects.get(id=sku_id)
    32         except GoodsSKU.DoesNoExist:
    33             return JsonResponse({'res':2,'errmsg':'商品不存在'})
    34         ##  校验数目是否正确
    35         try:
    36             count = int(count)
    37         except Exception:
    38             return JsonResponse({'res':3,'errmsg':'添加的数目格式有误'})
    39 
    40 
    41         ##  3、业务处理
    42         ##  将数据添加到Redis数据库中,hash格式: cart_key : sku_id   count
    43         conn = get_redis_connection('default')
    44         cart_key= 'cart_%d'%user.id
    45         ##  判断cart_key中是否存在值sku_id --->  存在返回count的字符串类型,不存在返回None
    46         cart_count = conn.hget(cart_key,sku_id)
    47         if cart_count:
    48             count += int(cart_count)
    49 
    50         ##  校验商品的库存,如果添加的数目大于库存则报错
    51         if count>sku.stock:
    52             return JsonResponse({'res':4,'errmsg':'库存不足'})
    53 
    54         ##  添加或更新到Redis数据库
    55         conn.hset(cart_key,sku_id,count)
    56         ##  计算购物车中的商品总条数
    57         total_count = conn.hlen(cart_key)
    58         return JsonResponse({'res':5,'message':'购物车添加成功','total_count':total_count})
    59 
    60 ##  使用LoginRequiredMiXin判断用户是否登录
    61 class CartInfoView(LoginRequiredMiXin,View):
    62     '''购物车视图'''
    63     def get(self,request):
    64         '''显示购物车视图'''
    65         ##  获取用户信息
    66         user = request.user
    67         ##  从Redis数据库中查询用户的购物车信息
    68         cart_key = 'cart_%d'%user.id
    69         conn = get_redis_connection('default')
    70         ##  查询用户的所有购物车信息  {商品sku_id:数目}
    71         cart_dict = conn.hgetall(cart_key)
    72 
    73         skus = []   ##  sku列表
    74         total_count = 0 ##  商品总数目
    75         total_price = 0 ##  所有商品的总价格
    76         ##  遍历查询到的字典
    77         for sku_id,count in cart_dict.items():
    78             ##  根据sku_id从商品模块中查询相应的商品
    79             sku = GoodsSKU.objects.get(id=sku_id)
    80             count = int(count)
    81             ##  动态给sku增加商品数目的属性
    82             sku.count = count
    83             ##  动态给sku增加商品总价的属性
    84             sku.amount = sku.price*count
    85             ##  将sku加入skus列表中
    86             skus.append(sku)
    87             ##  累加商品数目和价格
    88             total_count += sku.count
    89             total_price += sku.amount
    90 
    91         ##  组织模板上下文
    92         context = {
    93             'skus':skus,
    94             'total_count':total_count,
    95             'total_price':total_price
    96         }
    97 
    98         ##  返回应答
    99         return render(request,'cart.html',context)
    购物车添加的视图函数处理

    商品详情页的模板文件:

    {% extends 'base_detail_list.html' %}
    {% load staticfiles %}
    {% block title %}天天生鲜-商品详情{% endblock title %}
    
    {% block main_content %}
        <div class="breadcrumb">
            <a href="#">全部分类</a>
            <span>></span>
            <a href="#">{{ sku.type.name }}</a>
            <span>></span>
            <a href="#">商品详情</a>
        </div>
    
        <div class="goods_detail_con clearfix">
            <div class="goods_detail_pic fl"><img src="{{ sku.image.url }}"></div>
    
            <div class="goods_detail_list fr">
                <h3>{{ sku.name }}</h3>
                <p>{{ sku.desc }}</p>
                <div class="prize_bar">
                    <span class="show_pirze">¥<em>{{ sku.price }}</em></span>
                    <span class="show_unit">单  位:{{ sku.unite }}</span>
                </div>
                <div class="goods_num clearfix">
                    <div class="num_name fl">数 量:</div>
                    <div class="num_add fl">
                        <input type="text" class="num_show fl" value="1">
                        <a href="javascript:;" class="add fr">+</a>
                        <a href="javascript:;" class="minus fr">-</a>
                    </div>
                </div>
                <div>
                    <p>其他规格:</p>
                    <ul>
                        {% for sku in same_spu_skus %}
                            <li><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></li>
                        {% endfor %}
                    </ul>
                </div>
    
                <div class="total">总价:<em>16.80元</em></div>
                <div class="operate_btn">
                    {% csrf_token %}
                    <a href="javascript:;" class="buy_btn">立即购买</a>
                    <a href="javascript:;" sku_id="{{ sku.id }}" class="add_cart" id="add_cart">加入购物车</a>
                </div>
            </div>
        </div>
    
        <div class="main_wrap clearfix">
            <div class="l_wrap fl clearfix">
                <div class="new_goods">
                    <h3>新品推荐</h3>
                    <ul>
                        {% for sku in new_skus %}
                        <li>
                            <a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
                            <h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4>
                            <div class="prize">¥{{ sku.price }}</div>
                        </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
    
            <div class="r_wrap fr clearfix">
                <ul class="detail_tab clearfix">
                    <li class="active">商品介绍</li>
                    <li>评论</li>
                </ul>
    
                <div class="tab_content">
                    <dl>
                        <dt>商品详情:</dt>
                        <dd>{{ sku.goods.detail|safe }}</dd>
                    </dl>
                </div>
    
                <div class="tab_content">
                    <dl>
                        {% for order in sku_orders %}
                        <dt>评论时间:{{ order.update_time }}&nbsp;&nbsp;用户名:{{ order.order.user.username }}</dt>
                        <dd>评论内容:{{ order.comment }}</dd>
                        {% endfor %}
                    </dl>
                </div>
            </div>
        </div>
    {% endblock main_content %}
    {% block bottom %}
        <div class="add_jump"></div>
    {% endblock bottom %}
    {% block bottomfiles %}
        <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
        <script type="text/javascript">
            update_goods_amount()
            // 计算商品的总价格
            function update_goods_amount() {
                // 获取商品的单价和数量
                price = $('.show_pirze').children('em').text()
                count = $('.num_show').val()
                // 计算商品的总价
                price = parseFloat(price)
                count = parseInt(count)
                amount = price*count
                // 设置商品的总价
                $('.total').children('em').text(amount.toFixed(2)+'')
            }
    
            // 增加商品的数量
            $('.add').click(function () {
                // 获取商品原有的数目
                count = $('.num_show').val()
                // 加1
                count = parseInt(count)+1
                // 重新设置商品的数目
                $('.num_show').val(count)
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 减少商品的数量
            $('.minus').click(function () {
                // 获取商品原有的数目
                count = $('.num_show').val()
                // 减1
                count = parseInt(count)-1
                if (count <= 0){
                    count = 1
                }
                // 重新设置商品的数目
                $('.num_show').val(count)
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 手动输入商品的数量
            $('.num_show').blur(function () {
                // 获取用户输入的数目
                count = $(this).val()
                // 校验count是否合法
                if (isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
                    count = 1
                }
                // 重新设置商品的数目
                $(this).val(parseInt(count))
                // 更新商品的总价
                update_goods_amount()
            })
    
            // 获取add_cart div元素左上角的坐标
            var $add_x = $('#add_cart').offset().top;
            var $add_y = $('#add_cart').offset().left;
    
            // 获取show_count div元素左上角的坐标
            var $to_x = $('#show_count').offset().top;
            var $to_y = $('#show_count').offset().left;
    
    
            $('#add_cart').click(function(){
                // 获取商品id和商品数量
                sku_id = $(this).attr('sku_id') // attr prop
                count = $('.num_show').val()
                csrf = $('input[name="csrfmiddlewaretoken"]').val()
                // 组织参数
                params = {'sku_id':sku_id, 'count':count, 'csrfmiddlewaretoken':csrf}
                // 发起ajax post请求,访问/cart/add, 传递参数:sku_id count
                $.post('/cart/add', params, function (data) {
                    if (data.res == 5){
                        // 添加成功
                        $(".add_jump").css({'left':$add_y+80,'top':$add_x+10,'display':'block'})
                        $(".add_jump").stop().animate({
                            'left': $to_y+7,
                            'top': $to_x+7},
                            "fast", function() {
                                $(".add_jump").fadeOut('fast',function(){
                                    // 重新设置用户购物车中商品的条目数
                                    $('#show_count').html(data.total_count);
                                });
                        });
                    }
                    else{
                        // 添加失败
                        alert(data.errmsg)
                    }
                })
            })
        </script>
    {% endblock bottomfiles %}
    模板文件

    2、购物车页面记录的更新                                                                                                                                                                                                                             

    后端视图函数:

      1 from django.shortcuts import render
      2 from django.http import JsonResponse
      3 from django_redis import get_redis_connection
      4 from django.views.generic import View
      5 from goods.models import GoodsSKU
      6 from utils.mixin import LoginRequiredMiXin
      7 
      8 # Create your views here.
      9 
     10 
     11 ##  使用LoginRequiredMiXin判断用户是否登录
     12 class CartInfoView(LoginRequiredMiXin,View):
     13     '''购物车视图'''
     14     def get(self,request):
     15         '''显示购物车视图'''
     16         ##  获取用户信息
     17         user = request.user
     18         ##  从Redis数据库中查询用户的购物车信息
     19         cart_key = 'cart_%d'%user.id
     20         conn = get_redis_connection('default')
     21         ##  查询用户的所有购物车信息  {商品sku_id:数目}
     22         cart_dict = conn.hgetall(cart_key)
     23 
     24         skus = []   ##  sku列表
     25         total_count = 0 ##  商品总数目
     26         total_price = 0 ##  所有商品的总价格
     27         ##  遍历查询到的字典
     28         for sku_id,count in cart_dict.items():
     29             ##  根据sku_id从商品模块中查询相应的商品
     30             sku = GoodsSKU.objects.get(id=sku_id)
     31             count = int(count)
     32             ##  动态给sku增加商品数目的属性
     33             sku.count = count
     34             ##  动态给sku增加商品总价的属性
     35             sku.amount = sku.price*count
     36             ##  将sku加入skus列表中
     37             skus.append(sku)
     38             ##  累加商品数目和价格
     39             total_count += sku.count
     40             total_price += sku.amount
     41 
     42         ##  组织模板上下文
     43         context = {
     44             'skus':skus,
     45             'total_count':total_count,
     46             'total_price':total_price
     47         }
     48 
     49         ##  返回应答
     50         return render(request,'cart.html',context)
     51 
     52 ##  ajax post 提交数据,接收的参数 sku_id count
     53 class CartUpdateView(View):
     54     '''更新购物车'''
     55     def post(self,request):
     56         '''更新购物车'''
     57         ##  判断用户是否登录
     58         user = request.user
     59         if not user.is_authenticated:
     60             ##  用户未登录
     61             return JsonResponse({'res': 0, 'errmsg': '用户未登录'})
     62 
     63         ##  1、接收数据  传过来的参数: sku_id  count
     64         sku_id = request.POST.get('sku_id')
     65         count = request.POST.get('count')
     66 
     67         ##  2、数据校验
     68         ##  校验数据的完整性
     69         if not all([sku_id, count]):
     70             return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
     71         ##  校验商品是否存在
     72         try:
     73             sku = GoodsSKU.objects.get(id=sku_id)
     74         except GoodsSKU.DoesNoExist:
     75             return JsonResponse({'res': 2, 'errmsg': '商品不存在'})
     76         ##  校验数目是否正确
     77         try:
     78             count = int(count)
     79         except Exception:
     80             return JsonResponse({'res': 3, 'errmsg': '添加的数目格式有误'})
     81 
     82         ##  页面处理,更新Redis中购物车的数据
     83         conn = get_redis_connection('default')
     84         cart_key = 'cart_%d'%user.id
     85 
     86         ##  校验库存
     87         stock = sku.stock
     88         if count>stock:
     89             return JsonResponse({'res':4,'errmsg':'商品库存不足'})
     90 
     91         ##  更新Redis
     92         conn.hset(cart_key,sku.id,count)
     93 
     94         ##  计算商品的总数量
     95         total_count = 0
     96         vals = conn.hvals(cart_key)
     97         for val in vals:
     98             total_count += int(val)
     99 
    100         ##  返回应答
    101         return JsonResponse({'res':5,'massage':'购物车更新成功','total_count':total_count})
    102 
    103 
    104 class CartDeleteView(View):
    105     '''购物车记录删除'''
    106     def post(self,request):
    107         '''购物车记录删除'''
    108         ##  获取用户,判断用户是否登录,如果未登录则不能添加购物车
    109         user = request.user
    110         if not user.is_authenticated:
    111             ##  用户未登录
    112             return JsonResponse({'res': 0, 'errmsg': '用户未登录'})
    113 
    114         ##  数据接收
    115         sku_id = request.POST.get('sku_id')
    116 
    117         ##  数据校验
    118         try:
    119             sku_id = int(sku_id)
    120         except Exception:
    121             return JsonResponse({'res':0,'errmsg':'数据格式不正确'})
    122 
    123         try:
    124             sku = GoodsSKU.objects.get(id=sku_id)
    125         except GoodsSKU.DoesNoExist:
    126             return JsonResponse({'res':1,'errmsg':'无效的商品编号'})
    127 
    128         ##  业务处理
    129         cart_key = 'cart_%d'%user.id
    130         conn = get_redis_connection('default')
    131         ##  删除sku.id 这个商品
    132         conn.hdel(cart_key,sku.id)
    133 
    134         ##  计算商品的总数量
    135         total_count = 0
    136         vals = conn.hvals(cart_key)
    137         for val in vals:
    138             total_count += int(val)
    139 
    140         ##  返回应答
    141         return JsonResponse({'res':2,'massage':'删除成功','total_count':total_count})
    购物车页面记录更新的视图函数

    购物车页面模板文件:

    模板文件

     购物车页面中:后端主要涉及到Redis的查询与修改,前端主要涉及到ajax post请求

    参考文档:Redis文档

    订单模块------order

     1、订单页面的显示                                                                                                                                                                                                                              

    逻辑分析: 

      当购物车页面点击结算时,需要向后端用户订单页面  传递的参数: sku_id 组成的列表,request.POST.getlist()获取
    而后端的订单页面视图函数需要向订单页面前端传递的参数:skus sku数量和小计 总数量 总价格 用户关联的地址
     1 from django.shortcuts import render,redirect,reverse
     2 from django.views.generic import View
     3 from django.http import JsonResponse
     4 from django_redis import get_redis_connection
     5 from datetime import datetime
     6 from utils.mixin import LoginRequiredMiXin
     7 from goods.models import GoodsSKU
     8 from user.models import Address
     9 
    10 
    11 # Create your views here.
    12 
    13 ##  用户订单页面  传递的参数: sku_id 组成的列表,request.POST.getlist()获取
    14 ##  需要向订单页面前端传递的参数:skus sku数量和小计 总数量 总价格 用户关联的地址
    15 ##  需要先校验用户是否登录
    16 class OrderPlaceView(LoginRequiredMiXin,View):
    17     '''用户订单页面'''
    18     def post(self,request):
    19         '''用户订单页面'''
    20         ##  获取用户信息
    21         user = request.user
    22         ##  获取用户关联的收货地址
    23         addrs = Address.objects.filter(user=user)
    24 
    25         ##  1、接收参数
    26         skus_id = request.POST.getlist('sku_id')
    27 
    28         ##  2、校验参数
    29         if not skus_id:
    30             return redirect(reverse('cart:cart_info'))
    31 
    32         ##  3、业务处理
    33         ##  初始化商品总数量和总价格
    34         total_count = 0
    35         total_price = 0
    36         ##  将sku加入skus列表中
    37         skus = []
    38         ##  连接到Redis数据库
    39         conn = get_redis_connection('default')
    40         cart_key = 'cart_%d' % user.id
    41         ##  遍历获取sku
    42         for sku_id in skus_id:
    43             sku = GoodsSKU.objects.get(id=sku_id)
    44             skus.append(sku)
    45 
    46             ##  获取sku的数量
    47             count = conn.hget(cart_key,sku.id)
    48             count = int(count)
    49             ##  获取sku的小计
    50             amount = count*sku.price
    51             ##  动态给sku增加数量属性和小计属性
    52             sku.count = count
    53             sku.amount = amount
    54             ##  累加总数量和总价格
    55             total_count += count
    56             total_price += amount
    57 
    58 
    59 
    60         ##  设置运费
    61         trans_money = 10
    62 
    63         ##  实付款
    64         total_pay = total_price + trans_money
    65 
    66         ##  将skus_id拼接成一个 2,5 之类的字符串传到前端
    67         skus_id = ','.join(skus_id)
    68 
    69 
    70         ##  组织模板上下文
    71         context = {
    72             'skus':skus,
    73             'total_count':total_count,
    74             'total_price':total_price,
    75             'addrs':addrs,
    76             'trans_money':trans_money,
    77             'total_pay':total_pay,
    78             'skus_id':skus_id
    79         }
    80 
    81         ##  4、返回应答
    82         return render(request,'place_order.html',context)
    订单页面视图函数
    {% extends 'base_no_cart.html' %}
    {% load static %}
    {% block title %}天天生鲜-提交订单{% endblock title %}
    {% block page_title %}提交订单{%endblock page_title %}
    {% block body %}
        <h3 class="common_title">确认收货地址</h3>
    
        <div class="common_list_con clearfix">
            <dl>
                <dt>寄送到:</dt>
                {% for addr in addrs %}
                    <dd><input type="radio" name="addr_id" value="{{addr.id}}" {% if addr.is_default %} checked {% endif %}>{{ addr.addr }} ({{ addr.receiver }} 收) {{ addr.phone }}</dd>
                {% endfor %}
            </dl>
            <a href="user_center_site.html" class="edit_site">编辑收货地址</a>
    
        </div>
        
        <h3 class="common_title">支付方式</h3>    
        <div class="common_list_con clearfix">
            <div class="pay_style_con clearfix">
                <input type="radio" name="pay_style" checked value="1">
                <label class="cash">货到付款</label>
                <input type="radio" name="pay_style" value="2">
                <label class="weixin">微信支付</label>
                <input type="radio" name="pay_style" value="3">
                <label class="zhifubao"></label>
                <input type="radio" name="pay_style" value="4">
                <label class="bank">银行卡支付</label>
            </div>
        </div>
    
        <h3 class="common_title">商品列表</h3>
        
        <div class="common_list_con clearfix">
            <ul class="goods_list_th clearfix">
                <li class="col01">商品名称</li>
                <li class="col02">商品单位</li>
                <li class="col03">商品价格</li>
                <li class="col04">数量</li>
                <li class="col05">小计</li>        
            </ul>
            {% for sku in skus %}
                <ul class="goods_list_td clearfix">
                <li class="col01">{{ forloop.counter }}</li>
                <li class="col02"><img src="{{sku.image.url}}"></li>
                <li class="col03">{{ sku.name }}</li>
                <li class="col04">{{ sku.unite }}</li>
                <li class="col05">{{ sku.price }}元</li>
                <li class="col06">{{ sku.count }}</li>
                <li class="col07">{{ sku.amount }}元</li>
            </ul>
            {% endfor %}
        </div>
    
        <h3 class="common_title">总金额结算</h3>
    
        <div class="common_list_con clearfix">
            <div class="settle_con">
                <div class="total_goods_count"><em>{{ total_count }}</em>件商品,总金额<b>{{ total_price }}元</b></div>
                <div class="transit">运费:<b>{{ trans_money }}元</b></div>
                <div class="total_pay">实付款:<b>{{ total_pay }}元</b></div>
            </div>
        </div>
    
        <div class="order_submit clearfix">
            {% csrf_token %}
            <a href="javascript:;" skus_id={{ skus_id }} id="order_btn">提交订单</a>
        </div>    
    {% endblock body %}
    {% block bottom %}
        <div class="popup_con">
            <div class="popup">
                <p>订单提交成功!</p>
            </div>
            
            <div class="mask"></div>
        </div>
    {% endblock bottom %}
    
    {% block bottomfiles %}
        <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
        <script type="text/javascript">
            //    当提交按钮被点击时,需要向后台传递的参数有:支付方式  用户选择的收货地址id   skus_id
            $('#order_btn').click(function(){
                var addr_id = $('input[name="addr_id"]:checked').val();
                var pay_method = $('input[name="pay_style"]:checked').val();
                var skus_id = $(this).attr('skus_id');
                var csrf = $('input[name="csrfmiddlewaretoken"]').val();
    
                //    组织模板上下文
                var context = {
                    'addr_id':addr_id,
                    'pay_method':pay_method,
                    'skus_id':skus_id,
                    'csrfmiddlewaretoken':csrf
                };
    
                //    发去ajax post请求
                $.post('/order/commit',context,function(data){
                        if(data.res == 5){
                            //    订单创建成功
                            alert(data.message);
                        }
                        else{
                            alert(data.errmsg);
                        }
                });
            });
    
    
    
            $('#order_btn').click(function() {
                localStorage.setItem('order_finish',2);
    
                $('.popup_con').fadeIn('fast', function() {
    
                    setTimeout(function(){
                        $('.popup_con').fadeOut('fast',function(){
                            // window.location.href = 'index.html';
                        });
                    },3000)
    
                });
            });
        </script>
    {% endblock bottomfiles %}
    订单页面的模板文件

    2、创建新订单                                                                                                                                                                                                                              

    逻辑分析:

    创建新订单的视图函数需要接收的参数: 支付方式 pay_method       收货地址id  addr_id     购买的商品id  skus_id
    接收参数之后,需要先从df_order_info(订单数据)中插入一条数据,再向df_goods_info(商品订单)中插入相应的商品数据
    最后更新MySQL和Redis数据库中的数据

     1 ##  `/order/commit  接收的参数: 支付方式 pay_method       收货地址id  addr_id     购买的商品id  skus_id
     2 class OrderCommitView(View):
     3     '''订单创建'''
     4     def post(self,request):
     5         '''订单创建'''
     6         ##  判断用户是否登录
     7         user = request.user
     8         if not user:
     9             return JsonResponse({'res':0,'errmsg':'用户未登录'})
    10 
    11         ##  1、接收数据
    12         pay_method = request.POST.get('pay_method')
    13         addr_id = request.POST.get('addr_id')
    14         skus_id = request.POST.get('skus_id')
    15         ##  将skus_id的格式转化成列表形式
    16         skus_id = skus_id.split(',')
    17 
    18 
    19         ##  2、校验数据
    20         if not all([pay_method,addr_id,skus_id]):
    21             return JsonResponse({'res':1,'errmsg':'数据不完整'})
    22         skus = []
    23         for sku_id in skus_id:
    24             ##  遍历skus_id,从数据库中查询sku
    25             try:
    26                 sku = GoodsSKU.objects.get(id=sku_id)
    27                 skus.append(sku)
    28             except GoodsSKU.DoesNoExist:
    29                 return JsonResponse({'res':2,'errmsg':'商品不存在'})
    30         try:
    31             addr = Address.objects.get(id=addr_id)
    32         except Address.DoesNoExist:
    33             return JsonResponse({'res':3,'errmsg':'地址不存在'})
    34         ##  校验支付方式
    35         if str(pay_method) not in OrderInfo.PAY_METHODS.keys():
    36             return JsonResponse({'res':4,'errmsg':'错误的支付方式'})
    37 
    38         ##  3、业务处理
    39         ##########  (1)先向订单数据库中添加一条数据,准备数据:order_id、user、addr、pay_method、total_count、total_price、transit_price
    40         ##  计算total_count和total_price,先初始化为0,后面查询各个商品的数量和价格之后再进行累加,以及重新保存
    41         total_count = 0
    42         total_price = 0
    43         ##  生成order_id:格式:当前的时间+sku_id
    44         order_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)
    45         ##  添加数据,创建新的订单数据
    46         order = OrderInfo.objects.create(order_id=order_id,
    47                                  user=user,
    48                                  addr=addr,
    49                                  pay_method=pay_method,
    50                                  total_count=total_count,
    51                                  total_price=total_price,
    52                                  transit_price=10)
    53 
    54         ##########  (2)向商品订单中添加多条数据,准备数据:order、sku、count、price
    55         conn = get_redis_connection('default')
    56         cart_key = 'cart_%d'%user.id
    57         for sku in skus:
    58             count = conn.hget(cart_key,sku.id)
    59             price = sku.price
    60             OrderGoods.objects.create(order=order,
    61                                       sku=sku,
    62                                       count=count,
    63                                       price=price)
    64             ##  MySQL数据库的库存减少,销量增加
    65             sku.stock -= int(count)
    66             sku.sales += int(count)
    67             sku.save()
    68             ##  删除Redis数据库中添加到订单中的数据
    69             conn.hdel(cart_key,sku.id)
    70             ##  累加total_count 和total_price
    71             total_count += int(count)
    72             total_price += int(count)*int(price)
    73         ##  重新更新订单中的total_count和total_price
    74         order.total_count = total_count
    75         order.total_price = total_price
    76         order.save()
    77 
    78         ##  4、返回应答
    79         return JsonResponse({'res':5,'message':'创建订单成功'})
    订单创建的视图函数
    {% extends 'base_no_cart.html' %}
    {% load static %}
    {% block title %}天天生鲜-提交订单{% endblock title %}
    {% block page_title %}提交订单{%endblock page_title %}
    {% block body %}
        <h3 class="common_title">确认收货地址</h3>
    
        <div class="common_list_con clearfix">
            <dl>
                <dt>寄送到:</dt>
                {% for addr in addrs %}
                    <dd><input type="radio" name="addr_id" value="{{addr.id}}" {% if addr.is_default %} checked {% endif %}>{{ addr.addr }} ({{ addr.receiver }} 收) {{ addr.phone }}</dd>
                {% endfor %}
            </dl>
            <a href="user_center_site.html" class="edit_site">编辑收货地址</a>
    
        </div>
        
        <h3 class="common_title">支付方式</h3>    
        <div class="common_list_con clearfix">
            <div class="pay_style_con clearfix">
                <input type="radio" name="pay_style" checked value="1">
                <label class="cash">货到付款</label>
                <input type="radio" name="pay_style" value="2">
                <label class="weixin">微信支付</label>
                <input type="radio" name="pay_style" value="3">
                <label class="zhifubao"></label>
                <input type="radio" name="pay_style" value="4">
                <label class="bank">银行卡支付</label>
            </div>
        </div>
    
        <h3 class="common_title">商品列表</h3>
        
        <div class="common_list_con clearfix">
            <ul class="goods_list_th clearfix">
                <li class="col01">商品名称</li>
                <li class="col02">商品单位</li>
                <li class="col03">商品价格</li>
                <li class="col04">数量</li>
                <li class="col05">小计</li>        
            </ul>
            {% for sku in skus %}
                <ul class="goods_list_td clearfix">
                <li class="col01">{{ forloop.counter }}</li>
                <li class="col02"><img src="{{sku.image.url}}"></li>
                <li class="col03">{{ sku.name }}</li>
                <li class="col04">{{ sku.unite }}</li>
                <li class="col05">{{ sku.price }}元</li>
                <li class="col06">{{ sku.count }}</li>
                <li class="col07">{{ sku.amount }}元</li>
            </ul>
            {% endfor %}
        </div>
    
        <h3 class="common_title">总金额结算</h3>
    
        <div class="common_list_con clearfix">
            <div class="settle_con">
                <div class="total_goods_count"><em>{{ total_count }}</em>件商品,总金额<b>{{ total_price }}元</b></div>
                <div class="transit">运费:<b>{{ trans_money }}元</b></div>
                <div class="total_pay">实付款:<b>{{ total_pay }}元</b></div>
            </div>
        </div>
    
        <div class="order_submit clearfix">
            {% csrf_token %}
            <a href="javascript:;" skus_id={{ skus_id }} id="order_btn">提交订单</a>
        </div>    
    {% endblock body %}
    {% block bottom %}
        <div class="popup_con">
            <div class="popup">
                <p>订单提交成功!</p>
            </div>
            
            <div class="mask"></div>
        </div>
    {% endblock bottom %}
    
    {% block bottomfiles %}
        <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
        <script type="text/javascript">
            //    当提交按钮被点击时,需要向后台传递的参数有:支付方式  用户选择的收货地址id   skus_id
            $('#order_btn').click(function(){
                var addr_id = $('input[name="addr_id"]:checked').val();
                var pay_method = $('input[name="pay_style"]:checked').val();
                var skus_id = $(this).attr('skus_id');
                var csrf = $('input[name="csrfmiddlewaretoken"]').val();
    
                //    组织模板上下文
                var context = {
                    'addr_id':addr_id,
                    'pay_method':pay_method,
                    'skus_id':skus_id,
                    'csrfmiddlewaretoken':csrf
                };
    
                //    发去ajax post请求
                $.post('/order/commit',context,function(data){
                        if(data.res == 5){
                            //    订单创建成功
                            alert(data.message);
                        }
                        else{
                            alert(data.errmsg);
                        }
                });
            });
    
    
    
            $('#order_btn').click(function() {
                localStorage.setItem('order_finish',2);
    
                $('.popup_con').fadeIn('fast', function() {
    
                    setTimeout(function(){
                        $('.popup_con').fadeOut('fast',function(){
                            // window.location.href = 'index.html';
                        });
                    },3000)
    
                });
            });
        </script>
    {% endblock bottomfiles %}
    模板文件

    补充:                        

      当添加的商品数量大于数据库中的库存时,就不应该让订单创建成功,但是这里会有一个问题,订单虽然创建失败了,数据库中的df_order_goods虽然没有修改数据,但是df_order_info仍然添加了一条数据,显然这是不行的,所以就必须要使用到MySQL中的事务,订单页面的事务使用具体参考:https://www.cnblogs.com/maoxinjueluo/p/12883162.html

    3、订单并发与处理                                                                                                                                                                                                                             

    订单并发的处理详情见:订单并发处理

    用户模块追加------用户订单与订单的支付

    1、用户订单中心页面的显示:                                                                                                                                                                                                                             

    用户订单中心显示的视图函数:

     1 class OrderView(LoginRequiredMiXin,View):
     2     '''用户中心-订单页'''
     3     def get(self,request,page):
     4         '''显示用户订单页'''
     5         ##  1、准备参数:根据用户查询相应的订单列表 orders,每个订单中的商品sku 数量 ,价格 以及每个订单的各个商品小计
     6         user = request.user
     7         ##  获取用户的所有订单
     8         orders = OrderInfo.objects.filter(user=user).order_by('-create_time')
     9         ##  遍历所有的订单,获取每个订单里面每个商品的小计,动态添加
    10         for order in orders:
    11             order_skus = OrderGoods.objects.filter(order_id=order.order_id)
    12             for order_sku in order_skus:
    13                 ##  动态给每个订单中的每类商品增加小计的属性
    14                 order_sku.amount = int(order_sku.count)*int(order_sku.price)
    15             ##  动态给每个订单增加order_skus属性
    16             order.order_skus = order_skus
    17             ##  动态给每个订单增加支付状态
    18             order.status = OrderInfo.ORDER_STATUS[order.order_status]
    19 
    20         ##  分页显示
    21         ##  获取Paginator对象
    22         paginator = Paginator(orders,1)
    23         #   获取传过来的页码,并且校验
    24         try:
    25             page = int(page)
    26         except Exception:
    27             page = 1
    28         if page > paginator.num_pages:
    29             page = 1
    30 
    31         ##  页面上最多显示5页页码
    32         ##  总页数小于5,显示全部页数
    33         ##  当前页小于等于3,显示1-5页
    34         ##  当前页大于等于倒数第3,显示后5页
    35         ##  其它情况,显示当前页的前两页,当前页,后两页
    36         if paginator.num_pages < 6:
    37             pages = paginator.page_range
    38         elif page <= 3:
    39             pages = range(1, 6)
    40         elif (paginator.num_pages - page) <= 2:
    41             pages = range(paginator.num_pages - 4, paginator.num_pages + 1)
    42         else:
    43             pages = range(page - 2, page + 3)
    44 
    45         ##  获取page页的page实例对象
    46         order_pages = paginator.page(page)
    47 
    48         ##  2、组织上下文
    49         context = { 'pages':pages,
    50                    'order_pages':order_pages,
    51                    'page':'order'}
    52 
    53 
    54         return render(request,'user_center_order.html',context)
    视图函数

     用户订单中心的模板文件:

    {% extends 'base_user_center.html' %}
    {% load static %}
    {% block title %}天天生鲜-用户中心{% endblock title %}
    {% block right_content %}
            <div class="right_content clearfix">
                    <h3 class="common_title2">全部订单</h3>
                {% for order in order_pages %}
                    <ul class="order_list_th w978 clearfix">
                        <li class="col01">{{ order.create_time }}</li>
                        <li class="col02">订单号:{{ order.order_id }}</li>
                        <li class="col02 stress">{{ order.status }}</li>
                    </ul>
    
                    <table class="order_list_table w980">
                        <tbody>
                            <tr>
                                <td width="55%">
                                    {% for order_sku in order.order_skus %}
                                        <ul class="order_goods_list clearfix">
                                        <li class="col01"><a href="{% url 'goods:detail' order_sku.sku.id %}"><img src="{{ order_sku.sku.image.url }}" ></a></li>
                                        <li class="col02">{{ order_sku.sku.name }}<em>{{ order_sku.price }}元/{{ order_sku.sku.unite }}</em></li>
                                        <li class="col03">{{ order_sku.count }}</li>
                                        <li class="col04">{{ order_sku.amount }}元</li>
                                    </ul>
                                    {% endfor %}
                                </td>
                                <td width="15%">{{ order.total_price|add:order.transit_price }}(含运费:{{ order.transit_price }})元</td>
                                <td width="15%">{{ order.status }}</td>
                                <td width="15%"><a href="#" class="oper_btn">去付款</a></td>
                            </tr>
                        </tbody>
                    </table>
                {% endfor %}
    
                    <div class="pagenation">
                        {% if order_pages.has_previous %}
                            <a href="{% url 'user:order' order_pages.previous_page_number %}"><上一页</a>
                        {% endif %}
                        {% for pindex in pages %}
                            {% if pindex == order_pages.number %}
                                <a href="{% url 'user:order' pindex %}" class="active">{{ pindex }}</a>
                            {% else %}
                                <a href="{% url 'user:order' pindex %}">{{ pindex }}</a>
                            {% endif %}
                        {% endfor %}
                        {% if order_pages.has_next %}
                            <a href="{% url 'user:order' order_pages.next_page_number %}">下一页></a>
                        {% endif %}
                    </div>
            </div>
    {% endblock right_content %}
    模板文件

    2、订单支付与支付结果的查询                                                                                                                                                                                                                                                   

    订单支付与支付结果的查询参考:https://www.cnblogs.com/maoxinjueluo/p/12889863.html

    3、订单评价                                                                                                                                                                                                                     

    订单评价的视图函数:

     1 class OrderCommentView(LoginRequiredMiXin,View):
     2     '''订单评论'''
     3     def get(self, request, order_id):
     4         """提供评论页面"""
     5         user = request.user
     6 
     7         # 校验数据
     8         if not order_id:
     9             return redirect(reverse('user:order'))
    10 
    11         try:
    12             order = OrderInfo.objects.get(order_id=order_id, user=user)
    13         except OrderInfo.DoesNotExist:
    14             return redirect(reverse("user:order"))
    15 
    16         # 根据订单的状态获取订单的状态标题
    17         order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
    18 
    19         # 获取订单商品信息
    20         order_skus = OrderGoods.objects.filter(order_id=order_id)
    21         for order_sku in order_skus:
    22             # 计算商品的小计
    23             amount = order_sku.count*order_sku.price
    24             # 动态给order_sku增加属性amount,保存商品小计
    25             order_sku.amount = amount
    26         # 动态给order增加属性order_skus, 保存订单商品信息
    27         order.order_skus = order_skus
    28 
    29         # 使用模板
    30         return render(request, "order_comment.html", {"order": order})
    31 
    32     def post(self, request, order_id):
    33         """处理评论内容"""
    34         user = request.user
    35         # 校验数据
    36         if not order_id:
    37             return redirect(reverse('user:order'))
    38 
    39         try:
    40             order = OrderInfo.objects.get(order_id=order_id, user=user)
    41         except OrderInfo.DoesNotExist:
    42             return redirect(reverse("user:order"))
    43 
    44         # 获取评论条数
    45         total_count = request.POST.get("total_count")
    46         total_count = int(total_count)
    47 
    48         # 循环获取订单中商品的评论内容
    49         for i in range(1, total_count + 1):
    50             # 获取评论的商品的id
    51             sku_id = request.POST.get("sku_%d" % i) # sku_1 sku_2
    52             # 获取评论的商品的内容
    53             content = request.POST.get('content_%d' % i, '') # cotent_1 content_2 content_3
    54             try:
    55                 order_goods = OrderGoods.objects.get(order=order, sku_id=sku_id)
    56             except OrderGoods.DoesNotExist:
    57                 continue
    58 
    59             order_goods.comment = content
    60             order_goods.save()
    61 
    62         order.order_status = 5 # 已完成
    63         order.save()
    64 
    65         return redirect(reverse("user:order", kwargs={"page": 1}))
    订单评价视图函数

    模板文件:

    {% extends 'base_user_center.html' %}
    {% load staticfiles %}
    {% block title %}天天生鲜-用户中心{% endblock %}
    {% block page_title %}用户中心{% endblock page_title %}
    {% block right_content %}
            <div class="right_content clearfix">
                <h3 class="common_title2">订单评价</h3>
                    <ul class="order_list_th w978 clearfix">
                        <li class="col01">{{order.create_time}}</li>
                        <li class="col02">订单号:{{order.order_id}}</li>
                        <li class="col02 stress">{{order.status_name}}</li>
                    </ul>
                <form method="post">
                    {% csrf_token %}
                    {# 订单id #}
                    <input type="hidden" name="order_id" value="{{order.order_id}}">
                    {# 订单中有几个商品 #}
                    <input type="hidden" name="total_count" value="{{order.order_skus|length}}">
                    {% for order_sku in order.order_skus %}
                    <table class="order_list_table w980">
                        <tbody>
                            <tr>
                                <td width="80%">
                                    <ul class="order_goods_list clearfix">
                                        <li class="col01"><img src="{{ order_sku.sku.image.url }}"></li>
                                        <li class="col02">{{order_sku.sku.name}}<em>{{order_sku.price}}/{{order_sku.sku.unite}}</em></li>
                                        <li class="col03">{{order_sku.count}}</li>
                                    </ul>
                                </td>
                                <td width="20%">{{order_sku.amount}}元</td>
                            </tr>
                        </tbody>
                    </table>
                    <div class="site_con">
                        <input type="hidden" name="sku_{{forloop.counter}}" value="{{order_sku.sku.id}}">
                        <div class="form_group form_group2">
                            <label>评价内容:</label>
                            <textarea class="site_area" name="content_{{forloop.counter}}"></textarea>
                        </div>
                    </div>
                    {% endfor %}
                    <input type="submit" name="" value="提交" class="info_submit">
                </form>
            </div>
    {% endblock right_content %}
    订单评价模板文件

    三、项目部署

     详情见:https://www.cnblogs.com/maoxinjueluo/p/12898164.html

  • 相关阅读:
    C++ SDL2事件处理
    C++ SDL_Image配置
    C++ TinyXML库读写XML
    C++ libcurl库使用
    C++ 配置使用libcurl
    C++ 正则使用
    C++使用cJSON
    Vue通过状态为页面切换添加loading、为ajax加载添加loading
    移动端真机调试工具--DebugGap (VIDE)
    new Date(str)返回的时间结果在移动端比PC端快了8小时
  • 原文地址:https://www.cnblogs.com/maoxinjueluo/p/12815554.html
Copyright © 2011-2022 走看看