Django项目笔记_3

商品模块

FastDFS(分布式图片服务器)

  • FastDFS:
    • Tracker server:负载均衡和调度
    • Storage server:文件存储
  • 可解决:
    • 海量存储,存储容量扩展方便
    • 文件内容重复
  • 安装配置后
  • 启动:
    • trackersudo service fdfs_trackerd start
    • storagesudo service fdfs_storaged start
  • 上传:fdfs_upload_file /etc/fdfs/client.conf xxx
  • FastDFS踩坑
    1. 启动时报错:尝试用/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start 启动
    2. 配置ip时,不要用127.0.0.1 用外网的本机IP
    3. 上传文件报错时,尝试关闭系统防火墙:systemctl stop filewalld.service

nginx-1.8.1

  • 因为FastDFS获取文件时效率较低,可用nginx配合FastDFS

  • 安装配置

  • 启动:sudo /usr/local/nginx/sbin/nginx

  • 停止:sudo /usr/local/nginx/sbin/nginx -s stop

  • nginx踩坑

    1. 预编译可能会出错:尝试sudo apt-get install libpcre3-dev

    2. 然后编译安装:sudo make ----->sudo make install

    3. 启动时日志里错误:''http-mime_type.......''

      /etc/fdfs目录中无http.confmime.type

python客户端上传测试

  • 需要安装fdfs_client-py
from fdfs_client.client import Fdfs_client
client = Fdfs_client('/etc/fdfs/client.conf')
ret = client.upload_by_filename('test')

自定义文件存储类

  • 在项目中,我们需要把文件存入FastDFS中,所以我们需要自定义一个文件存储的类

    • utils包中单独建一个fdfs包,然后新建storage.py 自定义类

      from django.core.files.storage import Storage
      from fdfs_client.client import Fdfs_client
      from django.conf import settings
      
      class FdfsStorage(Storage):
          '''fast dfs文件存储类'''
          def __init__(self,client_conf=None,base_url=None):
              '''初始化'''
              if client_conf is None:
                  client_conf = settings.FDFS_CLIENT_CONF
              self.client_conf = client_conf
      
              if base_url is None:
                  base_url = settings.FDFS_URL
              self.base_url = base_url
      
          def _open(self,name,mode='rb'):
              '''打开文件时使用'''
              # 我们不需要打开文件
              pass
      
          def _save(self,name,content):
              '''保存文件时使用'''
              # name:选择上传文件的名字
              # content:包含上传文件内容的File对象
      
              # 创建一个Fdfs_client对象
              client = Fdfs_client(self.client_conf)
      
              # 上传文件到fast dfs系统中
              res = client.upload_appender_by_buffer(content.read())
              # dict
              # {
              #     'Group name': group_name,
              #     'Remote file_id': remote_file_id,
              #     'Status': 'Upload successed.',
              #     'Local file name': '',
              #     'Uploaded size': upload_size,
              #     'Storage IP': storage_ip
              # }
              if res.get('Status') != 'Upload successed.':
                  # 上传失败
                  raise Exception('上传文件到fast dfs失败')
              # 获取返回的文件ID
              filename = res.get('Remote file_id')
      
              return filename
      
          def exists(self, name):
              '''Django判断文件名是否可用'''
              return False
      
          def url(self, name):
              '''返回访问url'''
              return self.base_url+name
      
    • 自定义类之后,Django默认的文件存储类是FlieSystemStorage,需要使用自己定义的类,需在settings.py中更改配置

      # 设置Django文件存储类
      DEFAULT_FILE_STORAGE = 'utils.fdfs.storage.FdfsStorage'
      
      # 设置fdfs使用的client.conf文件路径
      FDFS_CLIENT_CONF = './utils/fdfs/client.conf'   # 最好复制一份配置文件放在项目里
      
      # 设置fdfs存储服务器上nginx的ip和端口号
      FDFS_URL = 'http://127.0.0.1:8888/'
      

首页页面静态化

  • 把原本动态的页面处理结果保存成html文件,让用户直接访问这个生成出来的静态的html页面

  • celery:用一个任务来生成一个静态页面

  • 什么时候静态页面需要生成:当管理员后台修改页面信息时

  • tasks.py 中定义一个方法来产生首页静态页面,里面跟view中查询一样,只不过后面是使用模板,然后生成相应的静态文件

    @app.task
    def generate_static_index_html():
        '''产生首页静态页面'''
        # 获取商品的种类信息
        types = GoodsType.objects.all()
    
        # 获取首页轮播商品信息
        goods_banners = IndexGoodsBanner.objects.all().order_by('index')
    
        # 获取首页促销活动信息
        promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
    
        # 获取首页分类商品展示信息
        for type in types:  # GoodsType
            # 获取type种类首页分类商品的图片展示信息
            image_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
            # 获取type种类首页分类商品的文字展示信息
            title_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0).order_by('index')
    
            # 动态给type增加属性,分别保存首页分类商品的图片展示信息和文字展示信息
            type.image_banners = image_banners
            type.title_banners = title_banners
    
    
        # 组织模板上下文
        context = {'types': types,
                   'goods_banners': goods_banners,
                   'promotion_banners': promotion_banners}
    
        # 使用模板
        # 1.加载模板文件,返回模板对象
        temp = loader.get_template('static_index.html')
        # 2.模板渲染
        static_index_html = temp.render(context)
        # print(static_index_html)
        # 生成首页对应静态文件
        print('*'*50)
        save_path = os.path.join(settings.BASE_DIR, 'static/index.html')
        print(save_path)
        with open(save_path, 'w') as f:
            f.write(static_index_html)
    

    注意的是,在从models导入类时,导入应放在django文件初始化下面,不然会找不到

  • 配置nginx提交静态页面

    • 在任务执行者电脑的**/usr/local/nginx/conf/nginx.conf**,在增减一个sever配置

      server {
                  listen 80;
                  server_name  localhost;
                  
                  location /static {
                      alias /home/fxh/python/project/dailyfresh/static/;
                  }
                  
                  location / {
                  	root /home/fxh/python/project/dailyfresh/static/;
                  	index index.html index.htm; 
                  }
               }
      
  • 静态页面和IndexView的调度

  • admin管理更新时,重新生成静态页面

    • 后台管理新增或更新页面时,会调用ModelAdmin.save_model(),删除时会调用ModelAdmin.delete_model()

    • 当我们需要更改页面后重新生成静态页面时,只要在上面两个方法中添加celery发出事先写好的任务即可,在admin.py中新定义一个类来修改两方法

      class BaseModelAdmin(admin.ModelAdmin):
          '''修改页面重新生成静态页面'''
      
          def save_model(self, request, obj, form, change):
              '''新增或更新表中的数据调用'''
              super().save_model(request, obj, form, change)
      
              # 发出任务,让celery work而重新生成首页静态页面
              from celery_tasks.tasks import generate_static_index_html
              generate_static_index_html.delay()
      
              # 清除首页的缓存数据
              # cache.delete('index_page_data')
      
          def delete_model(self, request, obj):
              '''删除表中的数据时'''
              super().delete_model(request, obj)
      
              # 发出任务,让celery work而重新生成首页静态页面
              from celery_tasks.tasks import generate_static_index_html
              generate_static_index_html.delay()
              
              # 清除首页的缓存数据
              cache.delete('index_page_data')
      
      class GoodsTypeAdmin(BaseModelAdmin):
          pass
      
      admin.site.register(GoodsType, GoodsTypeAdmin)
      

页面数据缓存

  • 把页面使用的数据放在缓存中,当再次使用这些数据时,先从缓存中获取,如果获取不到,再去查询数据库,减少数据库查询的次数,将缓存保存在redis

  • settings.py中设置缓存配置

    # Django的缓存的配置
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/9",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        }
    }
    
  • django三个缓存级别:参考文档

    • 站点级缓存:缓存整个网站,数据过大,不合适
    • 单个view缓存:不同用户首页内容不同,不合适
    • 模板片段的缓存:使用模板是在最后,之前数据都查完了,不合适
  • 三个缓存级别都不合适我们的要求,这时,我们可以操作底层缓存的API

    • cache有两个接口

      • 设置缓存:cache.set(key,value,timeout)(缓存名,缓存数据,过期时间(默认永久))
      • 获取:get(key)
    • 在view.py中,先尝试获取缓存,若缓存数据为None,则查询数据,设置缓存

      # 尝试从缓存中获取数据
      context = cache.get('index_page_data')
      
      if context is None:
          print('设置缓存')
          # 缓存中没有数据
          # 获取商品的种类信息
          types = GoodsType.objects.all()
      
          # 获取首页轮播商品信息
          goods_banners = IndexGoodsBanner.objects.all().order_by('index')
      
          # 获取首页促销活动信息
          promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
      
          # 获取首页分类商品展示信息
          for type in types: # GoodsType
              # 获取type种类首页分类商品的图片展示信息
              image_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
              # 获取type种类首页分类商品的文字展示信息
              title_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0).order_by('index')
      
              # 动态给type增加属性,分别保存首页分类商品的图片展示信息和文字展示信息
              type.image_banners = image_banners
              type.title_banners = title_banners
      
          context = {'types': types,
                     'goods_banners': goods_banners,
                     'promotion_banners': promotion_banners}
          # 设置缓存
          # key  value timeout
          cache.set('index_page_data', context, 3600)
      

用户浏览记录的添加

  • 首先徐判断记录是否有该商品,若有,先删除,然后在插入列表的左侧

    # 获取用户购物车中商品的数目
    user = request.user
    cart_count = 0
    if user.is_authenticated():
    	# 用户已登录
        conn = get_redis_connection('default')
        cart_key = 'cart_%d' % user.id
        art_count = conn.hlen(cart_key)
    
        # 添加用户的历史记录
        conn = get_redis_connection('default')
        history_key = 'history_%d'%user.id   # 键
        # 移除列表中的goods_id
        conn.lrem(history_key, 0, goods_id)
        # 把goods_id插入到列表的左侧
        conn.lpush(history_key, goods_id)
        # 只保存用户最新浏览的5条信息
        conn.ltrim(history_key, 0, 4)
    

    lrem:该方法会自行判断redis中有无该商品,若有则直接删除,若没有则不操作,0表示删除全部,**goods_id*删除的对象

商品列表页

  • 列表页url设计

    • /list/种类id/页码/排序方式

    • /list/种类id/页码?sort=排序方式(选择这种)

    • /list?type_id=种类id&page=页码&sort=排序方式

      url(r'^list/(?P<type_id>\d+)/(?P<page>\d+)$', ListView.as_view(), name='list'), # 列表页
      
  • 对数据进行分页 文档资料

    • Django有提供帮助管理分页数据的类,并带有”上一页“和”下一页“的标签,这些类在django/core/paginator.py------> Paginator

      Paginator(object_list, per_page)(数据列表,每页展示的条数)

      [外链图片转存失败(img-hoqLmWSm-1567068767074)(https://FXHao.github.io/images/posts/Django项目/图片11.png)]

      [外链图片转存失败(img-cCtDxZyl-1567068767076)(https://FXHao.github.io/images/posts/Django项目/图片12.png)]

  • 进行页码的控制,页面上最多显示5个页码

    • 1.总页数小于5页,页面上显示所有页码

    • 2.如果当前页是前3页,显示1-5页

    • 3.如果当前页是后3页,显示后5页

    • 4.其他情况,显示当前页的前2页,当前页,当前页的后2页

      num_pages = paginator.num_pages
      if num_pages < 5:
      	pages = range(1, num_pages+1)
      elif page <= 3:
      	pages = range(1, 6)
      elif num_pages - page <= 2:
      	pages = range(num_pages-4, num_pages+1)
      else:
      	pages = range(page-2, page+3)
      

商品搜索

  • 搜索引擎:可以对表中的某些字段进行关键词分析,建立关键词对应的索引数据

  • 全文检索框架:可以帮助用户使用搜索引擎

    [外链图片转存失败(img-3SO31fDt-1567068767093)(https://FXHao.github.io/images/posts/Django项目/图片13.png)]

  • 安装:

    pip install django-haystack
    pip install whoosh
    
  • 配置:注册应用,添加配置项

    # 全文检索框架的配置
    HAYSTACK_CONNECTIONS = {
        'default': {
            #使用whoosh引擎
            # 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
            'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
            #索引文件路径
            'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
        }
    }
    
  • 生成索引文件

    • 在商品模块中,新建一个名为search_indexes.py(名字固定)文件,在里面定义一个商品索引类

      # 定义索引类
      from haystack import indexes
      from .models import GoodsSKU
      
      # 指定对于某个类的某些数据建立索引
      # 索引类类名格式:模型类名+Index
      class GoodsInfoIndex(indexes.SearchIndex, indexes.Indexable):
          # 索引字段  use_template指定根据表中的哪些字段建立索引文件的说明放在一个文件中
          text = indexes.CharField(document=True, use_template=True)
      
          def get_model(self):
              return GoodsSKU
      
          # 建立索引的数据
          def index_queryset(self, using=None):
              return self.get_model().objects.all()
      
      
    • templates目录下建立search/indexes/goods/goodssku_text.txt,前两个名字是固定的,goods为应用模块名,goodssku_text.txt 前面部分的goodssku为模型类名的小写,后面固定

    • 然后在goodssku_text.txt文件里指定根据哪些字段建立索引

      # 指定根据表中的哪些字段建立索引数据
      {{ object.name }}  # 根据商品的名称建立索引
      {{ object.desc }}  # 根据商品的简介建立索引
      {{ object.goods.detail }}  # 根据商品的详情建立索引
      
    • 最后使用命令生成索引

      python manage.py rebuild_index
      
  • 全文检索的使用

    • 配置url(项目的urls.py)

      url(r'^search/', include('haystack.urls')), # 全文检索框架
      
    • 表单搜索时设置表单内容如下

      [外链图片转存失败(img-DwGtVWYZ-1567068767104)(https://FXHao.github.io/images/posts/Django项目/图片14.png)]

      要注意的是:name的值为q

    • 搜索出结果后,haystack会把搜索出的结果传递给templates/search目录下的search.html,所以我们还需创建该编辑文件,传递的上下文包括:

      • query:搜索关键字
      • page:当前页的page对象 -----> 遍历page对象,获取到的是****SearchResult类的实例对象,对象的属性object**才是模型类的对象。
      • paginator:分页paginator对象
    • 通过HAYSTACK_SEARCH_RESULTS_PER_PAGE 可以控制每页显示数量[外链图片转存失败(img-biYNSgnC-1567068767105)(https://FXHao.github.io/images/posts/Django项目/图片15.png)]

  • 改变分词方式

    • 原有的分词对中文会不太准确
    1. 安装jieba,这个分词模块对中文处理得比较好

      pip install jieba
      
    2. 找到虚拟环境下的haystack目录(在安装的包那找)

    3. 在上面的目录中创建ChineseAnalyzer.py文件

      import jieba
      from whoosh.analysis import Tokenizer, Token
      
      class ChineseTokenizer(Tokenizer):
          def __call__(self, value, positions=False, chars=False,
                       keeporiginal=False, removestops=True,
                       start_pos=0, start_char=0, mode='', **kwargs):
              t = Token(positions, chars, removestops=removestops, mode=mode, **kwargs)
              seglist = jieba.cut(value, cut_all=True)
              for w in seglist:
                  t.original = t.text = w
                  t.boost = 1.0
                  if positions:
                      t.pos = start_pos + value.find(w)
                  if chars:
                      t.startchar = start_char + value.find(w)
                      t.endchar = start_char + value.find(w) + len(w)
                  yield t
      
      def ChineseAnalyzer():
          return ChineseTokenizer()
      
    4. 复制一份whoosh_backend.py文件来修改, whoosh_cn_backend.py

    5. 打开复制出来的新文件,引入中文分析类,内部采用jieba分词

    from .ChineseAnalyzer import ChineseAnalyzer
    
    1. 更改词语分析类

      查找
      analyzer=StemmingAnalyzer()
      改为
      analyzer=ChineseAnalyzer()
      
    2. 修改settings.py文件中的配置项

      [外链图片转存失败(img-wYj3xbmF-1567068767115)(https://FXHao.github.io/images/posts/Django项目/图片16.png)]

    3. 重新创建索引文件

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值