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

原文地址:https://www.cnblogs.com/maoxinjueluo/p/12815554.html