Django项目实战——12—(Docker和FastDFS上传和下载文件、浏览器下载并渲染图片(nginx)、录入商品数据和图片数据、首页广告)

1、Docker和FastDFS上传和下载文件

Docker安装运行FastDFS

获取FastDFS镜像

# 从仓库拉取镜像
sudo docker image pull delron/fastdfs

# 解压资料中本地镜像
sudo docker load -i 文件路径/fastdfs_docker.tar

在这里插入图片描述

开启tracker容器

  • 我们将 tracker运行目录映射到宿主机的 /var/fdfs/tracker目录中。
sudo docker run -dit --name tracker --network=host -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker

在这里插入图片描述

开启storage容器

  • TRACKER_SERVER=Tracker的ip地址:22122Tracker的ip地址不要使用127.0.0.1
  • 我们将 storage运行目录映射到宿主机的/var/fdfs/storage目录中。
sudo docker run -dti --name storage --network=host -e TRACKER_SERVER=192.168.232.140:22122 -v /var/fdfs/storage:/var/fdfs delron/fastdfs storage

在这里插入图片描述
查看已经启动的storage和tracker
在这里插入图片描述

注意:如果无法重启storage容器,可以删除/var/fdfs/storage/data目录下的fdfs_storaged.pid文件,然后重新运行storage

FastDFS客户端上传文件

Python版本的FastDFS客户端使用参考文档:https://github.com/JaceHo/fdfs_client-py

  1. 安装FastDFS客户端扩展
    • 安装准备好的fdfs_client-py-master.zip到虚拟环境中
pip install fdfs_client-py-master.zip
pip install mutagen
pip install requests

从 Python版本的FastDFS客户端使用参考文档:https://github.com/JaceHo/fdfs_client-py中下载fdfs_client-py-master.zip文件,安装到项目的虚拟环境中:

Windows的Python环境引入fdfs_client-py包遇到的问题
https://blog.csdn.net/mingyuli/article/details/104171195

在这里插入图片描述
在这里插入图片描述

如果安装不成功,请参考:
Windows的Python环境引入fdfs_client-py包遇到的问题
https://blog.csdn.net/mingyuli/article/details/104171195
在这里插入图片描述
解决方法:
Fastdfs客户端ModuleNotFoundError: No module named 'mutagen._compat'问题
https://www.cnblogs.com/sewen-H/p/13362598.html
在这里插入图片描述

  1. 准备FastDFS客户端扩展的配置文件
    shop.utils.fastdfs.client.conf

FastDFS客户端扩展的配置文件:shop/utils/fastdfs/client.conf

"""
FastDFS客户端扩展的配置文件:shop/utils/fastdfs/client.conf
"""

# connect timeout in seconds
# default value is 30s
connect_timeout=30

# network timeout in seconds
# default value is 30s
network_timeout=60

# the base path to store log files
base_path=E:\ENV\Django_project_shop\shop\static\images                     # 本机路径

# tracker_server can ocur more than once, and tracker_server format is
#  "host:port", host can be hostname or ip address
tracker_server=192.168.232.140:22122                                        # 改为虚拟机ip

#standard log level as syslog, case insensitive, value list:
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level=info

# if use connection pool
# default value is false
# since V4.05
use_connection_pool = false

# connections whose the idle time exceeds this time will be closed
# unit: second
# default value is 3600
# since V4.05
connection_pool_max_idle_time = 3600

# if load FastDFS parameters from tracker server
# since V4.05
# default value is false
load_fdfs_parameters_from_tracker=false

# if use storage ID instead of IP address
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# default value is false
# since V4.05
use_storage_id = false

# specify storage ids filename, can use relative or absolute path
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# since V4.05
storage_ids_filename = storage_ids.conf


#HTTP settings
http.tracker_server_port=80

#use "#include" directive to include HTTP other settiongs
##include http.conf

注意:上面代码中需要修改的内容为

base_path=FastDFS客户端存放日志文件的目录
tracker_server=运行Tracker服务的机器ip:22122
  1. FastDFS客户端实现文件存储
# 使用 shell 进入 Python交互环境
python manage.py shell

在这里插入图片描述

# 1. 导入FastDFS客户端扩展
from fdfs_client.client import Fdfs_client

# 2. 创建FastDFS客户端实例   注意:windows环境下绝对路径会发生转义,需要加上 "r" 说明是原生字符串
client = Fdfs_client(r'绝对路径:shop/utils/fastdfs/client.conf')

# 3. 调用FastDFS客户端上传文件方法
ret = client.upload_by_filename('/kk.jpeg')

在创建FastDFS客户端实例
client = Fdfs_client(r'shop/utils/fastdfs/client.conf')
过程中,可能会出现:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xaf in position 19: illegal multibyte sequence的错误,
解决方案是:重新安装fdfs_client-py-master.zip文件,并修改其中的代码文件

在这里插入图片描述
在这里插入图片描述
创建FastDFS客户端实例出现报错Source contains parsing errors: [line 2]: '"""\n' [line 4]: '"""\n'或者unicodeescape’ codec can’t decode bytes in position XXX: trun
解决方案为:
在这里插入图片描述
上传文件ret = client.upload_by_filename(r’E:/1.jpg’)
报错:Error: 10061 connect to 192.168.xxx,由于目标计算机积极拒绝,无法连接
解决方案:https://blog.csdn.net/CaoMei_HuaCha/article/details/80865444

上传文件fdfs_client.exceptions.DataError: [-] Error: 2, No such file or directory报错问题
解决方案:https://blog.csdn.net/weixin_45336184/article/details/104289171

最终上传成功

在这里插入图片描述
虚拟机服务器查看文件:
在这里插入图片描述

2、浏览器下载并渲染图片(nginx)

  • 协议: http
  • IP地址:192.168.232.140 (虚拟机服务器ip) Nginx服务器的IP地址
  • 因为 FastDFS 擅长存储静态文件,但是不擅长提供静态文件的下载服务,所以我们一般会将 Nginx 服务器绑定到 Storage ,提升下载性能。
  • 端口:8888 -------Nginx服务器的端口。
  • 路径:group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png
    • 文件在Storage上的文件索引。
  • 完整图片下载地址
    http://192.168.232.140:8888/group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png

Nginx安装

sudo apt install nginx

编写测试代码:shop.utils.fdfs_t.html

<img src="http://192.168.232.140:8888/group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png" width="320" height="480">

安装nginx
在这里插入图片描述
虚拟机服务器
在这里插入图片描述

Win10中查看上传文件

在这里插入图片描述

成功查看上传的文件
在这里插入图片描述

3、录入商品数据和图片数据

SQL脚本录入商品数据

mysql -h127.0.0.1 -uroot -pmysql lgshop < 文件路径/goods_data.sql

FastDFS服务器录入图片数据

  1. 准备新的图片数据压缩包在这里插入图片描述

  2. 删除 Storage 中旧的data目录

在这里插入图片描述
3. 拷贝新的图片数据压缩包到 Storage,并解压

# 解压命令
sudo tar -zxvf data.tar.gz

在这里插入图片描述
4. 查看新的data目录
在这里插入图片描述

4、首页广告

展示首页商品频道分类

分析首页商品频道分类数据结构
在这里插入图片描述

查询首页商品频道分类

首页广告视图文件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       # 商品频道,商品类别


class IndexView(View):
    """首页广告界面"""
    
    def get(self, request):
        """提供首页界面"""
        # 查看并展示商品的分类
        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_id
            
            if group_id not in categories:         # category最顶级商品类别,如果当前频道不在顶级商品类别中
                categories[group_id] = {'channel': [], "sub_cats": []}       # 创建需要传输的数据格式
            
            # 当前频道对应的一级类别
            cat1 = channel.category                                # cat1是对象,category是外键字段
            categories[group_id]['channel'].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,
                    # })
                    
		context = {
            "categories": categories,         # 整体拼接后的数据
        }
        return render(request, "index.html", context=context)
   
    
"""需要形成如下的数据格式并传输给前端页面进行渲染
{
    "1":{
        # 频道
        "channels":[
            {"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
            {"id":2, "name":"相机", "url":"http://www.baidu.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":[]
    }
}
"""

渲染首页商品频道分类

首页界面templates/index.html

            {#  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>

展示首页商品广告

1.封装首页商品频道分类到文件

{    # 传输数据格式如下所示
    "index_lbt":[
        {
            "id":1, 
            "category": 1,
            "title": '美图',
            "url": 'http://www.baidu.com',
            "image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
            "text": "",
            "sequence": 1,
            "status": 1
        },
        {
            "id":2, 
            "category": 1,
            "title": '黑色星期五',
            "url": 'http://www.baidu.com',
            "image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
            "text": "",
            "sequence": 2,
            "status": 1
        }
    ],
    "index_kx":[
        {
            "id":5, 
            "category": 2,
            "title": 'i7顽石低至3999',
            "url": 'http://www.baidu.com',
            "image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
            "text": "",
            "sequence": 2,
            "status": 1
        },
        {
            "id":6, 
            "category": 2,
            "title":'奥克斯转场',
            "url": 'http://www.baidu.com',
            "image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
            "text": "",
            "sequence": 2,
            "status": 1
        }
    ]
}
"""

分析首页商品广告数据结构

结论:

  • 首页商品广告数据由广告分类和广告内容组成。
  • 广告分类带有标识符key,可以利用它确定广告展示的位置。
  • 确定广告展示的位置后,再查询和渲染出该位置的广告内容。
  • 广告的内容还有内部的排序字段,决定了广告内容的展示顺序。

查询首页商品广告:首页广告视图文件 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               # 广告内容类别,广告内容


class IndexView(View):
    """首页广告界面"""
    
    def get(self, request):
        """提供首页界面"""
        # 查看并展示商品的分类
        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_id
            
            if group_id not in categories:         # category最顶级商品类别,如果当前频道不在顶级商品类别中
                categories[group_id] = {'channel': [], "sub_cats": []}       # 创建需要传输的数据格式
            
            # 当前频道对应的一级类别
            cat1 = channel.category                                # cat1是对象,category是外键字段
            categories[group_id]['channel'].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,
                    # })
            
        # 查询所有的首页广告
        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)

渲染首页商品广告

  1. 轮播图广告

首页界面templates/index.html

        {#	轮播图的渲染	#}
        <ul class="slide">
            {#    apps/contents/views.py文件传输的contents对象,通过.index_lbt进行访问属性数据    #}
            {% for content in contents.index_lbt %}
                <li><a href="{{ content.url }}"><img src="{{ content.image }}" alt="{{ content.title }}"></a></li>
            {% endfor %}
        </ul>

前端界面显示效果如下:
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值