商品模块
FastDFS(分布式图片服务器)
- FastDFS:
- Tracker server:负载均衡和调度
- Storage server:文件存储
- 可解决:
- 海量存储,存储容量扩展方便
- 文件内容重复
- 安装配置后
- 启动:
- tracker:
sudo service fdfs_trackerd start
- storage:
sudo service fdfs_storaged start
- tracker:
- 上传:
fdfs_upload_file /etc/fdfs/client.conf xxx
- FastDFS踩坑
- 启动时报错:尝试用
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
启动 - 配置ip时,不要用
127.0.0.1
用外网的本机IP - 上传文件报错时,尝试关闭系统防火墙:
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踩坑
-
预编译可能会出错:尝试
sudo apt-get install libpcre3-dev
-
然后编译安装:
sudo make
—–>sudo make install
-
启动时日志里错误:
''http-mime_type.......''
在
/etc/fdfs
目录中无http.conf、mime.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)
(数据列表,每页展示的条数)
-
-
进行页码的控制,页面上最多显示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)
-
商品搜索
-
搜索引擎:可以对表中的某些字段进行关键词分析,建立关键词对应的索引数据
-
全文检索框架:可以帮助用户使用搜索引擎
-
安装:
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文件里指定根据哪些字段建立索引
# 指定根据表中的哪些字段建立索引数据 # 根据商品的名称建立索引 # 根据商品的简介建立索引 # 根据商品的详情建立索引
-
最后使用命令生成索引
python manage.py rebuild_index
-
-
全文检索的使用
-
配置url(项目的urls.py)
url(r'^search/', include('haystack.urls')), # 全文检索框架
-
表单搜索时设置表单内容如下
要注意的是:name的值为q
-
搜索出结果后,haystack会把搜索出的结果传递给
templates/search
目录下的search.html
,所以我们还需创建该编辑文件,传递的上下文包括:- query:搜索关键字
- page:当前页的page对象 —–> 遍历page对象,获取到的是SearchResult类的实例对象,对象的属性object**才是模型类的对象。
- paginator:分页paginator对象
-
通过HAYSTACK_SEARCH_RESULTS_PER_PAGE 可以控制每页显示数量
-
-
改变分词方式
- 原有的分词对中文会不太准确
-
安装jieba,这个分词模块对中文处理得比较好
pip install jieba
-
找到虚拟环境下的haystack目录(在安装的包那找)
-
在上面的目录中创建
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()
-
复制一份
whoosh_backend.py
文件来修改,whoosh_cn_backend.py
-
打开复制出来的新文件,引入中文分析类,内部采用jieba分词
from .ChineseAnalyzer import ChineseAnalyzer
-
更改词语分析类
查找 analyzer=StemmingAnalyzer() 改为 analyzer=ChineseAnalyzer()
-
修改settings.py文件中的配置项
-
重新创建索引文件