三、首页广告
首页数据由 商品频道分类 和 广告 组成
3.1 展示首页商品频道分类
3.1.1. 分析首页商品频道分类数据结构
{
"1":{
"channels":[
{"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
{"id":2, "name":"相机", "url":"http://www.itcast.cn/"}
],
"sub_cats":[
{
"id":38,
"name":"手机通讯",
"sub_cats":[
{"id":115, "name":"手机"},
{"id":116, "name":"游戏手机"}
]
},
{
"id":39,
"name":"手机配件",
"sub_cats":[
{"id":119, "name":"手机壳"},
{"id":120, "name":"贴膜"}
]
}
]
},
"2":{
"channels":[],
"sub_cats":[]
}
}
3.1.2. 查询首页商品频道分类
contents.view.py
from django.shortcuts import render
from django.views import View
from collections import OrderedDict # 有序字典
from meiduo_mall.apps.goods.models import GoodsChannel
# Create your views here.
class IndexView(View):
"""首页广告"""
def get(self, request):
"""提供首页广告界面"""
# 查询商品频道和分类
categories = OrderedDict()
channels = GoodsChannel.objects.order_by('group_id', 'sequence')
for channel in channels:
group_id = channel.group_id # 当前组
if group_id not in categories:
categories[group_id] = {'channels': [], 'sub_cats': []}
cat1 = channel.category # 当前频道的类别
# 追加当前频道
categories[group_id]['channels'].append({
'id': cat1.id,
'name': cat1.name,
'url': channel.url
})
# 构建当前类别的子类别
for cat2 in cat1.subs.all():
cat2.sub_cats = []
for cat3 in cat2.subs.all():
cat2.sub_cats.append(cat3)
categories[group_id]['sub_cats'].append(cat2)
# 渲染模板的上下文
context = {
'categories': categories,
}
return render(request, 'index.html', context)
3.1.3. 渲染首页商品频道分类
index.html
<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>
3.1.4. 封装首页商品频道分类
1.封装首页商品频道分类到contents.utils.py
文件
from collections import OrderedDict
from goods.models import GoodsChannel
def get_categories():
"""
提供商品频道和分类
:return 菜单字典
"""
# 查询商品频道和分类
categories = OrderedDict()
channels = GoodsChannel.objects.order_by('group_id', 'sequence')
for channel in channels:
group_id = channel.group_id # 当前组
if group_id not in categories:
categories[group_id] = {'channels': [], 'sub_cats': []}
cat1 = channel.category # 当前频道的类别
# 追加当前频道
categories[group_id]['channels'].append({
'id': cat1.id,
'name': cat1.name,
'url': channel.url
})
# 构建当前类别的子类别
for cat2 in cat1.subs.all():
cat2.sub_cats = []
for cat3 in cat2.subs.all():
cat2.sub_cats.append(cat3)
categories[group_id]['sub_cats'].append(cat2)
return categories
2.contents.view.py
中使用contents.utils.py
文件
class IndexView(View):
"""首页广告"""
def get(self, request):
"""提供首页广告界面"""
# 查询商品频道和分类
categories = get_categories()
# 广告数据
contents = {}
content_categories = ContentCategory.objects.all()
for cat in content_categories:
contents[cat.key] = cat.content_set.filter(status=True).order_by('sequence')
# 渲染模板的上下文
context = {
'categories': categories,
'contents': contents,
}
return render(request, 'index.html', context)
3.2 展示首页商品广告
3.2.1. 分析首页商品广告数据结构
结论:
- 首页商品广告数据由广告分类和广告内容组成。
- 广告分类带有标识符
key
,可以利用它确定广告展示的位置。- 确定广告展示的位置后,再查询和渲染出该位置的广告内容。
- 广告的内容还有内部的排序字段,决定了广告内容的展示顺序。
3.2.2. 查询首页商品广告
class IndexView(View):
"""首页广告"""
def get(self, request):
"""提供首页广告界面"""
# 查询商品频道和分类
......
# 广告数据
contents = {}
content_categories = ContentCategory.objects.all()
for cat in content_categories:
contents[cat.key] = cat.content_set.filter(status=True).order_by('sequence')
# 渲染模板的上下文
context = {
'categories': categories,
'contents': contents,
}
return render(request, 'index.html', context)
3.2.3. 渲染首页商品广告
1.轮播图广告
<ul class="slide">
{% for content in contents.index_lbt %}
<li><a href="{{ content.url }}"><img src="{{ content.image }}" alt="{{ content.title }}"></a></li>
{% endfor %}
</ul>
2.快讯和页头广告
<div class="news">
<div class="news_title">
<h3>快讯</h3>
<a href="#">更多 ></a>
</div>
<ul class="news_list">
{% for content in contents.index_kx %}
<li><a href="{{ content.url }}">{{ content.title }}</a></li>
{% endfor %}
</ul>
{% for content in contents.index_ytgg %}
<a href="{{ content.url }}" class="advs"><img src="{{ content.image }}"></a>
{% endfor %}
</div>
3.楼层广告(一楼)
<div class="list_model">
<div class="list_title clearfix">
<h3 class="fl" id="model01">1F 手机通讯</h3>
<div class="subtitle fr">
<a @mouseenter="f1_tab=1" :class="f1_tab===1?'active':''">时尚新品</a>
<a @mouseenter="f1_tab=2" :class="f1_tab===2?'active':''">畅想低价</a>
<a @mouseenter="f1_tab=3" :class="f1_tab===3?'active':''">手机配件</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="{{ contents.index_1f_logo.0.image}}">
<div class="channel">
{% for content in contents.index_1f_pd %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
<div class="key_words">
{% for content in contents.index_1f_bq %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
</div>
<div class="goods_list_con">
<ul v-show="f1_tab===1" class="goods_list fl">
{% for content in contents.index_1f_ssxp %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
<ul v-show="f1_tab===2" class="goods_list fl">
{% for content in contents.index_1f_cxdj %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
<ul v-show="f1_tab===3" class="goods_list fl">
{% for content in contents.index_1f_sjpj %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
4.楼层广告(二楼)
<div class="list_model model02">
<div class="list_title clearfix">
<h3 class="fl" id="model01">2F 电脑数码</h3>
<div class="subtitle fr">
<a @mouseenter="f2_tab=1" :class="f2_tab===1?'active':''">加价换购</a>
<a @mouseenter="f2_tab=2" :class="f2_tab===2?'active':''">畅享低价</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="{{ contents.index_2f_logo.0.image}}">
<div class="channel">
{% for content in contents.index_2f_pd %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
<div class="key_words">
{% for content in contents.index_2f_bq %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
</div>
<div class="goods_list_con">
<ul v-show="f2_tab===1" class="goods_list fl">
{% for content in contents.index_2f_cxdj %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
<ul v-show="f2_tab===2" class="goods_list fl">
{% for content in contents.index_2f_jjhg %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
5.楼层广告(三楼)
<div class="list_model model03">
<div class="list_title clearfix">
<h3 class="fl" id="model01">3F 家居家装</h3>
<div class="subtitle fr">
<a @mouseenter="f3_tab=1" :class="f3_tab===1?'active':''">生活用品</a>
<a @mouseenter="f3_tab=2" :class="f3_tab===2?'active':''">厨房用品</a>
</div>
</div>
<div class="goods_con clearfix">
<div class="goods_banner fl">
<img src="{{ contents.index_3f_logo.0.image }}">
<div class="channel">
{% for content in contents.index_3f_pd %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
<div class="key_words">
{% for content in contents.index_3f_bq %}
<a href="{{ content.url }}">{{ content.title }}</a>
{% endfor %}
</div>
</div>
<div class="goods_list_con">
<ul v-show="f3_tab===1" class="goods_list fl">
{% for content in contents.index_3f_shyp %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
<ul v-show="f3_tab===2" class="goods_list fl">
{% for content in contents.index_3f_cfyp %}
<li>
<a href="{{ content.url }}" class="goods_pic"><img src="{{ content.image }}"></a>
<h4><a href="{{ content.url }}" title="{{ content.title }}">{{ content.title }}</a></h4>
<div class="price">{{ content.text }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
3.3 自定义Django文件存储类
思考:
- 下图首页页面中图片无法显示的原因。
结论:
- 通过FastDFS上传文件后返回的
'Remote file_id'
字段是文件索引。- 文件索引会被我们存储到MySQL数据库。所以将来读取出来的也是文件索引,导致界面无法下载到图片。
解决:
- 重写Django文件存储类的url()方法。
- 在重写时拼接完整的图片下载地址(协议、IP、端口、文件索引)
3.3.1. Django文件存储类url()方法介绍
结论:
- 文件存储类
url()
方法的作用:返回name
所代表的文件内容的URL。- 文件存储类
url()
方法的触发:content.image.url
- 虽然表面上调用的是
ImageField
的url
方法。但是内部会去调用文件存储类的url()
方法。- 文件存储类
url()
方法的使用:
- 我们可以通过自定义Django文件存储类达到重写
url()
方法的目的。- 自定义Django文件存储类必须提供
url()
方法。- 返回name所指的文件对应的绝对URL。
3.3.2. 自定义Django文件存储类
自定义文件存储类的官方文档:https://docs.djangoproject.com/en/1.11/howto/custom-file-storage/
utils.fastdfs.fdfs_storage.py
from django.core.files.storage import Storage
class FastDFSStorage(Storage):
"""自定义文件存储系统"""
def _open(self, name, mode='rb'):
"""
用于打开文件
:param name: 要打开的文件的名字
:param mode: 打开文件方式
:return: None
"""
# 打开文件时使用的,此时不需要,而文档告诉说明必须实现,所以pass
pass
def _save(self, name, content):
"""
用于保存文件
:param name: 要保存的文件名字
:param content: 要保存的文件的内容
:return: None
"""
# 保存文件时使用的,此时不需要,而文档告诉说明必须实现,所以pass
pass
3.3.3. 重写Django文件存储类url()方法
1.重写url()
方法
from django.core.files.storage import Storage
from django.conf import settings
class FastDFSStorage(Storage):
"""自定义文件存储系统,修改存储的方案"""
def __init__(self, fdfs_base_url=None):
"""
构造方法,可以不带参数,也可以携带参数
:param base_url: Storage的IP
"""
self.fdfs_base_url = fdfs_base_url or settings.FDFS_BASE_URL
def _open(self, name, mode='rb'):
......
def _save(self, name, content):
......
def url(self, name):
"""
返回name所指文件的绝对URL
:param name: 要读取文件的引用:group1/M00/00/00/wKhnnlxw_gmAcoWmAAEXU5wmjPs35.jpeg
:return: http://192.168.103.158:8888/group1/M00/00/00/wKhnnlxw_gmAcoWmAAEXU5wmjPs35.jpeg
"""
# return 'http://192.168.103.158:8888/' + name
# return 'http://image.meiduo.site:8888/' + name
return self.fdfs_base_url + name
2.相关配置参数
# 指定自定义的Django文件存储类
DEFAULT_FILE_STORAGE = 'meiduo_mall.utils.fastdfs.fdfs_storage.FastDFSStorage'
# FastDFS相关参数
# FDFS_BASE_URL = 'http://192.168.103.158:8888/'
FDFS_BASE_URL = 'http://image.meiduo.site:8888/'
3.添加访问图片的域名
- 在
/etc/hosts
中添加访问Storage的域名
$ Storage的IP 域名
$ 192.168.103.158 image.meiduo.site
【这个地方写的IP地址是图片所在位置的IP地址】
4.文件存储类url()
方法的使用
- 以图片轮播图为例:
content.image.url
<ul class="slide">
{% for content in contents.index_lbt %}
<li><a href="{{ content.url }}"><img src="{{ content.image.url }}" alt="{{ content.title }}"></a></li>
{% endfor %}
</ul>
【在项目中所有image后都要加url】【简单方法是:ctr+f,查找.imaage后添加,以防丢失】
【有时候图片还是不显示,是因为tracker容器和storage容器没有启动,另外注意启动storage的IP地址是否动态发生变化,若是这样需要删除storage容器重新启动】