Django项目实战——13—(自定义Django文件存储类、商品列表页、列表页面包屑导航、列表页分页和排序)

news/2024/11/6 13:46:49/

1、自定义Django文件存储类

上一篇博客中介绍了首页轮播图的显示,由于因为域名的问题,图片无法访问虚拟机中的storage容器中存储的data数据。

结论:

  • 通过FastDFS上传文件后返回的Remote file_id字段是文件索引。
  • 文件索引会被我们存储到MySQL数据库。所以将来读取出来的也是文件索引,导致界面无法下载到图片。

解决:

  • 重写Django文件存储类的url()方法。
  • 在重写时拼接完整的图片下载地址(协议、IP、端口、文件索引)

Django文件存储类url()方法介绍

在这里插入图片描述
结论:

  • 文件存储类url()方法的作用:返回name所代表的文件内容的URL。
  • 文件存储类url()方法的触发:content.image.url
    • 虽然表面上调用的是ImageFieldurl方法。但是内部会去调用文件存储类的url()方法。
  • 文件存储类url()方法的使用:
    • 我们可以通过自定义Django文件存储类达到重写url()方法的目的。
    • 自定义Django文件存储类必须提供url()方法。
    • 返回name所指的文件对应的绝对URL

自定义Django文件存储类

自定义文件存储类的官方文档https://docs.djangoproject.com/en/2.2/howto/custom-file-storage/

重写文件存储继承的类utils/fastdfs/fdfs_storage.py

# -*- encoding: utf-8 -*-
"""
@File    : fdfs_storage.py
@Time    : 2020/9/7 15:55
@Author  : chen重写文件存储继承的类:utils/fastdfs/fdfs_storage.py
"""
from django.core.files.storage import Storage
from django.conf import settingsclass FastDFSStorage(Storage):"""自定义文件存储系统,修改存储的方案"""def __init__(self, FDFS_BASE_URL=None):# if not FDFS_BASE_URL:#     self.FDFS_BASE_URL = settings.FDFS_BASE_URL         # 如果参数中没有链接地址,使用dev.py文件中配置的# else:#     self.FDFS_BASE_URL = FDFS_BASE_URL                      # 传参有链接地址,使用其本身self.FDFS_BASE_URL = FDFS_BASE_URL or settings.FDFS_BASE_URL    # 功能与上面相同,代码简化#  _open 和_save方法在Storage类源码中已经存在,所以也不用重写,但是是重写的必添加项def _open(self, name, mode='rb'):"""用于打开文件:param name: 要打开的文件的名字:param mode: 打开文件方式:return: None"""# 打开文件时使用的,此时不需要,而文档告诉说明必须实现,所以passpassdef _save(self, name, content):"""用于保存文件:param name: 要保存的文件名字:param content: 要保存的文件的内容:return: None"""# 保存文件时使用的,此时不需要,而文档告诉说明必须实现,所以passpass# 重写的重要方法是url()def url(self, name):"""需要返回的路由: http://192.168.232.141:8888/group1/M00/00/01/CtM3BVrLmnaADtSKAAGlxZuk7uk4998927:param name:文件名称,也是路由,相当于是storage中的:group1/M00/00/01/CtM3BVrLmnaADtSKAAGlxZuk7uk4998927:return: 返回路由进行拼接:“http://192.168.232.141:8888/” + name"""# 拼接路由return self.FDFS_BASE_URL + name            # FDFS_BASE_URL是dev.py文件中的配置参数

开发环境配置文件dev.py

# 指定自定义的Django文件存储类
DEFAULT_FILE_STORAGE = 'utils.fastdfs.fdfs_storage.FastDFSStorage'
# FastDFS相关参数
# FDFS_BASE_URL = "http://192.168.232.141:8888/"
FDFS_BASE_URL = 'http://image.meiduo.site:8888/'      # 可以在/etc/hosts中添加访问Storage的域名进行绑定

可以添加访问图片的域名
• 在/etc/hosts中添加访问Storage的域名

$ Storage的IP        域名
$ 192.168.232.141    image.meiduo.site

在这里插入图片描述

文件存储类url()方法的使用

• 以图片轮播图为例:content.image.url

首页界面templates/index.html

{# 首页界面:templates/index.html #}
{% load static %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>LG商城-首页</title><link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}"><script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script><script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script><script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
</head>
<body><div id="app">
<div class="header_con" v-cloak>    {#	  v-cloak加载显示的问题    #}<div class="header"><div class="welcome fl">欢迎来到LG商城!</div><div class="fr">{#          方法一     登录用户的名称显示    #}
{#                {% if user.is_authenticated %}#}
{#                    <div class="login_btn fl">#}
{#                        欢迎您:<em>{{ user.username }}</em>#}
{#                        <span>|</span>#}
{#                        <a href="#">退出</a>#}
{#                    </div>#}
{#                {% else %}        {#   用户未登录,显示为    #}
{#                    <div class="login_btn fl">#}
{#                        <a href="login.html">登录</a>#}
{#                        <span>|</span>#}
{#                        <a href="register.html">注册</a>#}
{#				    </div>#}
{#                {% endif %}#}{#      方法三: Vue读取cookie渲染用户信息    v-if="username"有值就显示 #}<div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span>
{#                       绑定url,users是实例命名空间         #}<a href="{% url 'users:logout' %}">退出</a></div>{#          v-else                       #}<div v-else class="login_btn fl"><a href="{% url 'users:login' %}">登录</a><span>|</span><a href="{% url 'users:register' %}">注册</a></div><div class="user_link fl"><span>|</span><a href="{% url 'users:info' %}">用户中心</a><span>|</span><a href="cart.html">我的购物车</a><span>|</span><a href="user_center_order.html">我的订单</a></div></div></div></div><div class="search_bar clearfix"><a href="index.html" class="logo fl"><img src="{% static 'images/1.png' %}"></a><div class="search_wrap fl"><form method="get" action="/search/" class="search_con"><input type="text" class="input_text fl" name="q" placeholder="搜索商品"><input type="submit" class="input_btn fr" name="" value="搜索"></form><ul class="search_suggest fl"><li><a href="#">索尼微单</a></li><li><a href="#">优惠15元</a></li><li><a href="#">美妆个护</a></li><li><a href="#">买2免1</a></li></ul></div><div class="guest_cart fr"><a href="cart.html" class="cart_name fl">我的购物车</a><div class="goods_count fl" id="show_count">2</div><ul class="cart_goods_show"><li><img src="../static/images/goods/goods001.jpg" alt="商品图片"><h4>华为 HUAWEI P10 Plus 6GB+64GB 钻雕金 移动联通电信4G手机 双卡双待</h4><div>1</div></li><li><img src="../static/images/goods/goods002.jpg" alt="商品图片"><h4>Apple iPhoneX 64GB 深空灰色 移动联通电信4G手机</h4><div>1</div></li></ul></div></div><div class="navbar_con"><div class="navbar"><h1 class="fl">商品分类</h1>{#  apps/contents/views.py文件中传递的categories数据进行渲染	#}<ul class="sub_menu">{% for group in categories.values %}<li>{#      一级查询      #}<div class="level1">{% for channel in group.channels %}<a href="{{ channel.url }}">{{ channel.name }}</a>{% endfor %}</div><div class="level2">{#      二级查询      #}{% for cat2 in group.sub_cats %}<div class="list_group"><div class="group_name fl">{{ cat2.name }} &gt;</div><div class="group_detail fl">{#      三级查询      #}{% for cat3 in cat2.sub_cats %}<a href="/list/{{ cat3.id }}/1/">{{ cat3.name }}</a>{% endfor %}</div></div>{% endfor %}</div></li>{% endfor %}</ul><ul class="navlist fl"><li><a href="">首页</a></li><li class="interval">|</li><li><a href="">真划算</a></li><li class="interval">|</li><li><a href="">抽奖</a></li></ul></div></div><div class="pos_center_con clearfix">{#	轮播图的渲染	#}<ul class="slide">{#    apps/contents/views.py文件传输的contents对象,通过.index_lbt进行访问属性数据    #}{% for content in contents.index_lbt %}{# http://192.168.232.141:8888/{{ content.image }}修改为content.image.url #}<li><a href="{{ content.url }}"><img src="{{ content.image.url }}" alt="{{ content.title }}"></a></li>{% endfor %}</ul>......

2、商品列表页

在这里插入图片描述

商品列表页组成结构分析

  1. 商品频道分类
    • 已经提前封装在contents.utils.py文件中,直接调用即可。
  2. 面包屑导航
    • 可以使用三级分类ID,查询出该类型商品的三级分类数据。
  3. 排序和分页
    • 无论如何排序和分页,商品的分类不能变。
    • 排序时需要知道当前排序方式。
    • 分页时需要知道当前分页的页码,且每页五条商品记录。
  4. 热销排行
    • 热销排行中的商品分类要和排序、分页的商品分类一致。
    • 热销排行是查询出指定分类商品销量前二的商品。
    • 热销排行使用Ajax实现局部刷新的效果。

商品列表页接口设计和定义

  1. 请求方式
    在这里插入图片描述
# 按照商品创建时间排序
http://www.meiduo.site:8000/list/115/1/?sort=default
# 按照商品价格由低到高排序
http://www.meiduo.site:8000/list/115/1/?sort=price
# 按照商品销量由高到低排序
http://www.meiduo.site:8000/list/115/1/?sort=hot
  1. 请求参数:路径参数 和 查询参数
    在这里插入图片描述
  2. 响应结果:HTML
    list.html
  3. 接口定义
class GoodsListView(View):"""商品列表页"""def get(self, request, category_id, page_num):"""提供商品列表页"""return render(request, 'list.html')

项目实例代码

封装方法文件,代码复用apps/contents/utils.py

# -*- encoding: utf-8 -*-
"""
@File    : utils.py
@Time    : 2020/9/10 10:40
@Author  : chen封装方法文件,代码复用:apps/contents/utils.py
"""
from goods.models import GoodsChannel, GoodsCategory       # 商品频道,商品类别# 封装方法  获得所有商品方法
def get_categories():# 查看并展示商品的分类categories = {}# 查询所有的商品频道# channels = GoodsChannel.objects.all()# 37个一级类别channels = GoodsChannel.objects.order_by('group_id', 'sequence')  # 根据group_id排序,再根据组内顺序sequence排序# 遍历所有频道for channel in channels:group_id = channel.group_idif group_id not in categories:  # category最顶级商品类别,如果当前频道不在顶级商品类别中categories[group_id] = {'channels': [], "sub_cats": []}  # 创建需要传输的数据格式,注意channels的字段信息# 当前频道对应的一级类别cat1 = channel.category  # cat1是对象,category是外键字段categories[group_id]['channels'].append({  # channel本身包含字典数据"id": cat1.id,"name": cat1.name,  # category外键关联的name字段"url": channel.url})# 查询二级和三级类别信息for cat2 in GoodsCategory.objects.filter(parent__id=cat1.id).all():  # 通过频道对应的一级类别来查询cat2.sub_cats = []  # 创建是为了方便cat3进行添加数据categories[group_id]['sub_cats'].append({"id": cat2.id,"name": cat2.name,  # cat2对象本身模型GoodsCategory的name字段"sub_cats": cat2.sub_cats})# 查询三级类别信息for cat3 in GoodsCategory.objects.filter(parent__id=cat2.id).all():  # 通过二级类别来查询三级cat2.sub_cats.append({  # 方便cat3进行添加数据到cat2的list中"id": cat3.id,"name": cat3.name,})# categories[group_id]['sub_cats'][].append({            # 这种方法添加数据,sub_cats数据无法添加#     "id": cat3.id,#     "name": cat3.name,# })return categories

首页广告视图文件 apps/contents/views.py

"""
首页广告视图文件 apps/contents/views.py
"""
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from collections import OrderedDict         # 有序的字典,不同于普通字典
from goods.models import GoodsChannel, GoodsCategory       # 商品频道,商品类别
from .models import ContentCategory, Content               # 广告内容类别,广告内容
from .utils import get_categoriesclass IndexView(View):"""首页广告界面"""def get(self, request):"""提供首页界面"""# 查看并展示商品的分类categories = get_categories()               # 封装后的方法# 查询所有的首页广告context_categories = ContentCategory.objects.all()contents = {}for context_categorie in context_categories:# contents是一个对象,根据category外键进行查询,status=True限制条件,order_by('sequence')按照sequence排序contents[context_categorie.key] = Content.objects.filter(category__id=context_categorie.id,status=True).order_by('sequence')context = {"categories": categories,         # 商品频道整体拼接后的数据"contents": contents,             # 首页广告信息拼接结果}return render(request, "index.html", context=context)

商品模块的路由apps/goods/urls.py

# -*- encoding: utf-8 -*-
"""
@File    : urls.py
@Time    : 2020/9/10 10:03
@Author  : chen商品模块的路由:apps/goods/urls.py
"""
from django.urls import path, include, re_path
from . import viewsapp_name = 'goods'urlpatterns = [re_path(r'^list/(?P<category_id>\d+)/(?P<page_num>\d+)/$', views.GoodsListView.as_view(), name='list'),
]

项目总路由文件shop/urls.py

'''
项目总路由文件:shop/urls.py
'''from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('users/', include('users.urls')),    # 如果当时注册users模块时候没有使用sys.path.insert导入路径,这里就需要改为 'apps.users.urls'path('', include('contents.urls')),       # 首页路由path('', include('verifications.urls')),  # 验证码路由path('', include('oauth.urls')),          # QQ登陆路由path('', include('areas.urls')),          # 用户地址模块路由path('', include('goods.urls'))           # 商品模块总路由]

商品列表界面templates/list.html

{# 商品列表界面:templates/list.html #}
{% load static %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>LG商城-商品列表</title><link rel="stylesheet" type="text/css" href="{% static 'css/jquery.pagination.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}"><script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script><script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script><script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
</head>
<body><div id="app"><div class="header_con"><div class="header" v-cloak><div class="welcome fl">欢迎来到LG商城!</div><div class="fr"><div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span><a href="{% url 'users:logout' %}">退出</a></div><div v-else class="login_btn fl"><a href="{% url 'users:login' %}">登录</a><span>|</span><a href="{% url 'users:register' %}">注册</a></div><div class="user_link fl"><span>|</span><a href="{% url 'users:info' %}">用户中心</a><span>|</span><a href="cart.html">我的购物车</a><span>|</span><a href="user_center_order.html">我的订单</a></div></div></div></div><div class="search_bar clearfix"><a href="{% url 'contents:index' %}" class="logo fl"><img src="{% static 'images/1.png' %}"></a><div class="search_wrap fl"><form method="get" action="/search/" class="search_con"><input type="text" class="input_text fl" name="q" placeholder="搜索商品"><input type="submit" class="input_btn fr" name="" value="搜索"></form><ul class="search_suggest fl"><li><a href="#">索尼微单</a></li><li><a href="#">优惠15元</a></li><li><a href="#">美妆个护</a></li><li><a href="#">买2免1</a></li></ul></div><div class="guest_cart fr"><a href="cart.html" class="cart_name fl">我的购物车</a><div class="goods_count fl" id="show_count">2</div><ul class="cart_goods_show"><li><img src="{% static 'images/goods/goods001.jpg' %}" alt="商品图片"><h4>华为 HUAWEI P10 Plus 6GB+64GB 钻雕金 移动联通电信4G手机 双卡双待</h4><div>1</div></li><li><img src="{% static 'images/goods/goods002.jpg' %}" alt="商品图片"><h4>Apple iPhoneX 64GB 深空灰色 移动联通电信4G手机</h4><div>1</div></li></ul>			</div></div><div class="navbar_con"><div class="navbar"><div class="sub_menu_con fl"><h1 class="fl">商品分类</h1>{#  apps/goods/views.py文件中传递的categories数据进行渲染	#}<ul class="sub_menu">{% for group in categories.values %}<li>{#      一级查询      #}<div class="level1">{% for channel in group.channels %}<a href="{{ channel.url }}">{{ channel.name }}</a>{% endfor %}</div><div class="level2">{#      二级查询      #}{% for cat2 in group.sub_cats %}<div class="list_group"><div class="group_name fl">{{ cat2.name }} &gt;</div><div class="group_detail fl">{#      三级查询      #}{% for cat3 in cat2.sub_cats %}<a href="/list/{{ cat3.id }}/1/">{{ cat3.name }}</a>{% endfor %}</div></div>{% endfor %}</div></li>{% endfor %}</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><div class="breadcrumb"><a href="http://shouji.jd.com/">手机</a><span>></span><a href="javascript:;">手机配件</a><span>></span><a href="javascript:;">手机壳</a></div><div class="main_wrap clearfix"><div class="l_wrap fl clearfix"><div class="new_goods"><h3>热销排行</h3><ul><li><a href="detail.html"><img src="{% static 'images/goods/goods001.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="price">¥3999.90</div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods002.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="price">¥1666.80</div></li></ul></div></div><div class="r_wrap fr clearfix"><div class="sort_bar"><a href="/list/115/1/?sort=default" class="active">默认</a><a href="/list/115/1/?sort=price">价格</a><a href="/list/115/1/?sort=hot">人气</a></div><ul class="goods_type_list clearfix"><li><a href="detail.html"><img src="{% static 'images/goods/goods003.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="operate"><span class="price">¥1666.80</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods005.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="operate"><span class="price">¥1000.00</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods002.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="operate"><span class="price">¥2888.80</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods003.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="operate"><span class="price">¥1666.80</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods005.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="operate"><span class="price">¥1000.00</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li></ul></div></div><div class="footer"><div class="foot_link"><a href="#">关于我们</a><span>|</span><a href="#">联系我们</a><span>|</span><a href="#">招聘人才</a><span>|</span><a href="#">友情链接</a>		</div><p>CopyRight © 2016 北京LG商业股份有限公司 All Rights Reserved</p><p>电话:010-****888    京ICP备*******8号</p></div></div><script type="text/javascript">let category_id = "";</script><script type="text/javascript" src="{% static 'js/common.js' %}"></script><script type="text/javascript" src="{% static 'js/list.js' %}"></script><script type="text/javascript" src="{% static 'js/jquery.pagination.min.js' %}"></script><script>$(function () {$('#pagination').pagination({currentPage: 1,totalPage: 3,callback:function (current) {{#location.href = '/list/115/1/?sort=default';#}{#location.href = '/list/{{ category.id }}/' + current + '/?sort={{ sort }}';#}}})});</script>
</body>
</html>

商品列表界面的静态文件:static/js/list.js

// 商品列表界面的静态文件:static/js/list.js
let vm = new Vue({el: '#app',delimiters: ['[[', ']]'],data: {username: getCookie('username'),category_id: category_id,hot_skus: [],cart_total_count: 0,carts: [],},mounted(){// 获取热销商品数据this.get_hot_skus();// 获取简单购物车数据// this.get_carts();},methods: {// 获取热销商品数据get_hot_skus(){if (this.category_id) {let url = '/hot/'+ this.category_id +'/';axios.get(url, {responseType: 'json'}).then(response => {this.hot_skus = response.data.hot_skus;for(let i=0; i<this.hot_skus.length; i++){this.hot_skus[i].url = '/detail/' + this.hot_skus[i].id + '/';}}).catch(error => {console.log(error.response);})}},// 获取简单购物车数据get_carts(){let url = '/carts/simple/';axios.get(url, {responseType: 'json',}).then(response => {this.carts = response.data.cart_skus;this.cart_total_count = 0;for(let i=0;i<this.carts.length;i++){if (this.carts[i].name.length>25){this.carts[i].name = this.carts[i].name.substring(0, 25) + '...';}this.cart_total_count += this.carts[i].count;}}).catch(error => {console.log(error.response);})},}
});

3、列表页面包屑导航

重要提示:路径参数category_id是商品第三级分类

查询列表页面包屑导航数据

封装方法文件,代码复用apps/goods/utils.py

# -*- encoding: utf-8 -*-
"""
@File    : utils.py
@Time    : 2020/9/10 11:20
@Author  : chen封装方法文件,代码复用:apps/goods/utils.py
"""# 封装获得面包屑导航方法
def get_breadcrumb(category):"""获取面包屑导航:param category: 商品类别:return: 面包屑导航字典"""breadcrumb = dict(cat1='',cat2='',cat3='')if category.parent is None:            # 数据库信息查询,一级类别的parent_id为None# 当前类别为一级类别breadcrumb['cat1'] = categoryelif category.subs.count() == 0:       ## 当前类别为三级breadcrumb['cat3'] = categorycat2 = category.parentbreadcrumb['cat2'] = cat2breadcrumb['cat1'] = cat2.parentelse:# 当前类别为二级breadcrumb['cat2'] = categorybreadcrumb['cat1'] = category.parentreturn breadcrumb

商品视图文件apps/goods/views.py

"""
商品视图文件:apps/goods/views.py
"""
from django.shortcuts import render
from django.views import View
from contents.utils import get_categories         # 导入封装方法
from .utils import get_breadcrumb                 # 导入面包屑封装方法
from .models import GoodsCategory                 # 导入模型class GoodsListView(View):"""商品列表页面"""def get(self, request, category_id, page_num):"""查询并渲染商品"""# category_id 是商品分类的id# 查询商品的类别categories = get_categories()try:# 查询面包屑category = GoodsCategory.objects.get(id=category_id)except Exception as e:return render(request, '404.html')breadcrumb = get_breadcrumb(category)                     # 调用封装的方法# print(breadcrumb)context = {"categories": categories,"breadcrumb": breadcrumb,}return render(request, 'list.html', context=context)        # 传递数据到前端界面list.html

不存在文件界面文件templates/404.html

{#  不存在文件界面文件:templates/404.html  #}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>404</title><link rel="stylesheet" href="{% static 'css/reset.css' %}"><style>.logo{position:fixed;left:50%;margin-left:-500px;top:20px;			}.missing_pic{display:block;margin:70px auto 0px;}.tip{text-align:center;font-size:22px;font-weight:bold;color:#333;margin-top:20px;}.back_to_index{display:block;width:130px;line-height:30px;border-radius:15px;border:1px solid #ff3f3c;background:#ff3f3c;color:#fff;text-align:center;margin:20px auto 0;}.back_to_index:hover{opacity:0.6}</style>
</head>
<body><a href="{% url 'contents:index' %}" class="logo"><img src="{% static 'images/1.png' %}" alt="logo"></a><img src="{% static 'images/missing.png' %}" alt="404" class="missing_pic"><h3 class="tip">Oops!你访问的页面未找到</h3><a href="{% url 'contents:index' %}" class="back_to_index">返回首页</a>
</body>
</html>

渲染列表页面包屑导航数据

商品列表界面templates/list.html

 {#  面包屑标题展示:传输数据breadcrumb是由商品视图文件:apps/goods/views.py文件传输过来  #}<div class="breadcrumb">{#   breadcrumb.cat1.name  一级标题     #}<a href="http://shouji.jd.com/">{{ breadcrumb.cat1.name }}</a>{% if breadcrumb.cat2.name %}<span>></span><a href="javascript:;">{{ breadcrumb.cat2.name }}</a>{% endif %}{#   三级标题     #}{% if breadcrumb.cat3.name %}<span>></span><a href="javascript:;">{{ breadcrumb.cat3.name }}</a>{% endif %}</div>

4、列表页分页和排序

# 按照商品创建时间排序
http://www.meiduo.site:8000/list/115/1/?sort=default
# 按照商品价格由低到高排序
http://www.meiduo.site:8000/list/115/1/?sort=price
# 按照商品销量由高到低排序
http://www.meiduo.site:8000/list/115/1/?sort=hot

项目实例代码

项目报错
在这里插入图片描述
在这里插入图片描述
商品视图文件apps/goods/views.py

"""
商品视图文件:apps/goods/views.py
"""
from django.shortcuts import render
from django.views import View
from contents.utils import get_categories         # 导入封装方法
from .utils import get_breadcrumb                 # 导入面包屑封装方法
from .models import GoodsCategory, SKU            # 导入模型
from django.views.generic import ListView          # 分页
from django.core.paginator import Paginator        # 分页class GoodsListView(View):"""商品列表页面"""def get(self, request, category_id, page_num):"""查询并渲染商品"""# category_id 是商品分类的idtry:# 查询面包屑category = GoodsCategory.objects.get(id=category_id)except Exception as e:return render(request, '404.html')# 获取排序规则  接收sort参数:如果用户不传,就是默认的排序规则sort = request.GET.get('sort', 'default')     # 根据字段sort查询if sort == 'price':                # 按照排序规则查询该分类商品SKU信息sort_field = 'price'           # 根据字段price的正序排列,价格由低到高elif sort == 'hot':sort_field = '-sales'          # 根据字段sales的倒序排列,销量由高到低else:                              # 当传输的排序方式不是上面的两种,需要让排序方式为默认的sort = 'default'sort_field = 'create_time'     # 根据create_time字段排序# 排序      根据category.id筛选,以sort_field排序skus = SKU.objects.filter(is_launched=True, category_id=category.id).order_by(sort_field)# print(skus)# 分页  Paginator('分页面的记录', '每页记录的条数')  创建分页器paginator = Paginator(skus, 5)# 获取page_num的页码的数据page_skus = paginator.page(page_num)# 总的页面数tatal_page = paginator.num_pages# 查询商品的类别categories = get_categories()breadcrumb = get_breadcrumb(category)                     # 调用封装的方法# print(breadcrumb)context = {"categories": categories,       # 频道分类"breadcrumb": breadcrumb,       # 面包屑导航"page_skus": page_skus,         # 分页后数据"tatal_page": tatal_page,       # 总页数"category_id": category_id,     # 商品类别id"sort": sort,                   # 排序字段,以何种方式排序"page_num": page_num,           # 当前页码}return render(request, 'list.html', context=context)        # 传递数据到前端界面list.html

商品列表界面templates/list.html

{# 商品列表界面:templates/list.html #}
{% load static %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>LG商城-商品列表</title><link rel="stylesheet" type="text/css" href="{% static 'css/jquery.pagination.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}"><script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script><script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script><script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
</head>
<body><div id="app"><div class="header_con"><div class="header" v-cloak><div class="welcome fl">欢迎来到LG商城!</div><div class="fr"><div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span><a href="{% url 'users:logout' %}">退出</a></div><div v-else class="login_btn fl"><a href="{% url 'users:login' %}">登录</a><span>|</span><a href="{% url 'users:register' %}">注册</a></div><div class="user_link fl"><span>|</span><a href="{% url 'users:info' %}">用户中心</a><span>|</span><a href="cart.html">我的购物车</a><span>|</span><a href="user_center_order.html">我的订单</a></div></div></div></div><div class="search_bar clearfix"><a href="{% url 'contents:index' %}" class="logo fl"><img src="{% static 'images/1.png' %}"></a><div class="search_wrap fl"><form method="get" action="/search/" class="search_con"><input type="text" class="input_text fl" name="q" placeholder="搜索商品"><input type="submit" class="input_btn fr" name="" value="搜索"></form><ul class="search_suggest fl"><li><a href="#">索尼微单</a></li><li><a href="#">优惠15元</a></li><li><a href="#">美妆个护</a></li><li><a href="#">买2免1</a></li></ul></div><div class="guest_cart fr"><a href="cart.html" class="cart_name fl">我的购物车</a><div class="goods_count fl" id="show_count">2</div><ul class="cart_goods_show"><li><img src="{% static 'images/goods/goods001.jpg' %}" alt="商品图片"><h4>华为 HUAWEI P10 Plus 6GB+64GB 钻雕金 移动联通电信4G手机 双卡双待</h4><div>1</div></li><li><img src="{% static 'images/goods/goods002.jpg' %}" alt="商品图片"><h4>Apple iPhoneX 64GB 深空灰色 移动联通电信4G手机</h4><div>1</div></li></ul>			</div></div><div class="navbar_con"><div class="navbar"><div class="sub_menu_con fl"><h1 class="fl">商品分类</h1>{#  apps/goods/views.py文件中传递的categories数据进行渲染	#}<ul class="sub_menu">{% for group in categories.values %}<li>{#      一级查询      #}<div class="level1">{% for channel in group.channels %}<a href="{{ channel.url }}">{{ channel.name }}</a>{% endfor %}</div><div class="level2">{#      二级查询      #}{% for cat2 in group.sub_cats %}<div class="list_group"><div class="group_name fl">{{ cat2.name }} &gt;</div><div class="group_detail fl">{#      三级查询      #}{% for cat3 in cat2.sub_cats %}<a href="/list/{{ cat3.id }}/1/">{{ cat3.name }}</a>{% endfor %}</div></div>{% endfor %}</div></li>{% endfor %}</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>{#  面包屑标题展示:传输数据breadcrumb是由商品视图文件:apps/goods/views.py文件传输过来  #}<div class="breadcrumb">{#   breadcrumb.cat1.name  一级标题     #}<a href="http://shouji.jd.com/">{{ breadcrumb.cat1.name }}</a>{% if breadcrumb.cat2.name %}<span>></span><a href="javascript:;">{{ breadcrumb.cat2.name }}</a>{% endif %}{#   三级标题     #}{% if breadcrumb.cat3.name %}<span>></span><a href="javascript:;">{{ breadcrumb.cat3.name }}</a>{% endif %}</div><div class="main_wrap clearfix"><div class="l_wrap fl clearfix"><div class="new_goods"><h3>热销排行</h3><ul><li><a href="detail.html"><img src="{% static 'images/goods/goods001.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="price">¥3999.90</div></li><li><a href="detail.html"><img src="{% static 'images/goods/goods002.jpg' %}"></a><h4><a href="detail.html">360手机 N6 Pro 全网通 6GB+128GB 极夜黑</a></h4><div class="price">¥1666.80</div></li></ul></div></div><div class="r_wrap fr clearfix"><div class="sort_bar">{#     category_id商品类别信息传输给url链接     1代表第一页    下面链接的另一种写法:/list/{{ category_id }}/1/?sort=default    #}<a href="{% url 'goods:list' category_id=category_id page_num=1 %}?sort=default" {% if sort == 'default' %} class="active"{% endif %}>默认</a><a href="{% url 'goods:list' category_id=category_id page_num=1 %}?sort=price" {% if sort == 'price' %} class="active"{% endif %}>价格</a><a href="{% url 'goods:list' category_id=category_id page_num=1 %}?sort=hot" {% if sort == 'hot' %} class="active"{% endif %}>人气</a></div><ul class="goods_type_list clearfix">{#  循环views.py文件传递的参数page_skus,当前页面的数据 #}{% for sku in page_skus %}<li>
{# sku.default_image_url获得的图片链接,default_image_url字段数据类型需要是models.ImageField,.url属性是utils/fastdfs/fdfs_storage.py文件中重写的url()方法 #}<a href="detail.html"><img src="{{ sku.default_image_url.url }}"></a>{#           sku 模型下的name字段            #}<h4><a href="detail.html">{{ sku.name }}</a></h4><div class="operate">{#           sku 模型下的price字段            #}<span class="price">¥{{ sku.price }}</span><span class="unit"></span><a href="#" class="add_goods" title="加入购物车"></a></div></li>{% endfor %}</ul>{#    准备分页器标签    #}<div class="pagenation"><div id="pagination" class="page"></div></div></div></div><div class="footer"><div class="foot_link"><a href="#">关于我们</a><span>|</span><a href="#">联系我们</a><span>|</span><a href="#">招聘人才</a><span>|</span><a href="#">友情链接</a>		</div><p>CopyRight © 2016 北京LG商业股份有限公司 All Rights Reserved</p><p>电话:010-****888    京ICP备*******8号</p></div></div><script type="text/javascript">
{#     js文件中调用商品类别id ---> category_id  #}let category_id = "{{ category_id }}";</script><script type="text/javascript" src="{% static 'js/common.js' %}"></script><script type="text/javascript" src="{% static 'js/list.js' %}"></script><script type="text/javascript" src="{% static 'js/jquery.pagination.min.js' %}"></script><script>$(function () {$('#pagination').pagination({// 当前所在的页码   page_num、tatal_page、category_id参数是views.py文件传输而来currentPage: {{ page_num }},totalPage: {{ tatal_page }},callback:function (current) {{#location.href = '/list/115/1/?sort=default';#}{#     分页链接        #}location.href = '/list/{{ category_id }}/' + current + '/?sort={{ sort }}';}})});</script>
</body>
</html>

http://www.ppmy.cn/news/206567.html

相关文章

二十六

首页新闻博问专区闪存班级 我的博客我的园子账号设置退出登录注册登录BWH_Steven 博客园首页新随笔联系订阅管理随笔 - 112 文章 - 0 评论 - 20【设计模式】第七篇&#xff1a;和我一起简单认识桥接模式 【设计模式】第七篇&#xff1a;和我一起简单…

我又准备转行了

说一些心里话。 公众号最近这两个月我更新地很少&#xff0c;一是懒&#xff0c;二是忙&#xff0c;三是心里一直在想一个问题&#xff1a;我以后要写些什么内容。 想了很久&#xff0c;到今天我想明白了&#xff0c;那就是去写我真正喜欢和擅长的内容&#xff1a;摄影和旅行。…

美多商城之商品(2)

美多商城之商品&#xff08;2&#xff09; 1.商品搜索1.1.全文检索方案Elasticsearch1.2.Haystack扩展建立索引1.3.渲染商品搜索结果 2.商品详情页2.1.商品详情页分析和准备2.2.展示详情页数据2.3.统计分类商品访问量 3.用户浏览记录3.1.设计浏览记录存储方案3.2.保存和查询浏览…

25 岁,毕业写前端的这三年

我加入美团点评的面试很顺利&#xff0c;最主要的流程就是我把上面的一些各色玩具和相应的博客和前端老板虎哥秀了一下吧。在那里&#xff0c;我的主要工作是开发一个名为学城的内部知识库系统。虽然我为这个项目提交的代码占比可能已经所剩无几&#xff0c;但我相信只要它还活…

【论文简述及翻译】MVSNet:Depth Inference for Unstructured Multi-view Stereo(ECCV 2018)

一、论文简述 1. 第一作者&#xff1a;Yao Yao 2. 发表年份&#xff1a;2018 Oral 3. 发表期刊&#xff1a;ECCV 4. 关键词&#xff1a;MVS、端到端网络、代价体、深度图、可微分单应变换 5. 探索动机&#xff1a;传统方法存在一些常见的局限性&#xff0c;很难处理场景的…

华为全新MateBook E评测

屏幕方面&#xff0c;华为MateBook E此次在业内率先使用了一块12.6英寸的OLED原彩触控全面屏&#xff0c;屏占比达到90%&#xff0c;采用16:10比例&#xff0c;分辨率为2K&#xff08;2560*1600&#xff09;级别&#xff0c;对比度高达1,000,000:1&#xff0c;最高亮度600尼特&…

华为鸿蒙状态栏,华为手机状态栏图标都代表什么

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 以华为P40为例&#xff0c;状态栏图标代表的意思如下&#xff1a; 1、WIFI网络&#xff1a;代表WIFI功能开启、关闭与连接状态。 2、蓝牙&#xff1a;代表蓝牙功能是否开启。 3、移…

华为平板鸿蒙,鸿蒙OS骁龙870加持 华为MatePad Pro 10.8曝光

近日&#xff0c;华为已经正式宣布将于6月2日召开鸿蒙操作系统及华为全场景新品发布会&#xff0c;届时大家期待已久的鸿蒙OS正式版将正式登场。 根据官方透露的信息&#xff0c;此次发布会还将推出新一代的华为MatePad Pro旗舰平板电脑&#xff0c;该设备也将成为预装鸿蒙OS正…