1、自定义Django文件存储类
上一篇博客中介绍了首页轮播图的显示,由于因为域名的问题,图片无法访问虚拟机中的storage容器
中存储的data
数据。
结论:
- 通过
FastDFS
上传文件后返回的Remote file_id
字段是文件索引。 - 文件索引会被我们存储到
MySQL
数据库。所以将来读取出来的也是文件索引,导致界面无法下载到图片。
解决:
- 重写
Django
文件存储类的url()
方法。 - 在重写时拼接完整的图片下载地址(协议、IP、端口、文件索引)
Django文件存储类url()方法介绍
结论:
- 文件存储类
url()
方法的作用:返回name
所代表的文件内容的URL。 - 文件存储类
url()
方法的触发:content.image.url
• 虽然表面上调用的是ImageField
的url
方法。但是内部会去调用文件存储类的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 }} ></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、商品列表页
商品列表页组成结构分析
- 商品频道分类
• 已经提前封装在contents.utils.py文件中,直接调用即可。 - 面包屑导航
• 可以使用三级分类ID,查询出该类型商品的三级分类数据。 - 排序和分页
• 无论如何排序和分页,商品的分类不能变。
• 排序时需要知道当前排序方式。
• 分页时需要知道当前分页的页码,且每页五条商品记录。 - 热销排行
• 热销排行中的商品分类要和排序、分页的商品分类一致。
• 热销排行是查询出指定分类商品销量前二的商品。
• 热销排行使用Ajax实现局部刷新的效果。
商品列表页接口设计和定义
- 请求方式
# 按照商品创建时间排序
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
- 请求参数:路径参数 和 查询参数
- 响应结果:HTML
list.html
- 接口定义
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 }} ></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 }} ></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>