本教程的知识点为: 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后端接口设计: 用户部分 JWT 什么是JWT 起源 传统的session认证 用户部分 登录 1. 业务说明 2. 后端接口设计 3. 后端实现 登录 使用登录的流程 创建模型类 urllib使用说明 登录回调处理 登录 使用登录的流程 创建模型类 urllib使用说明 绑定用户身份接口 邮件与验证 学习目标: 业务说明: 技术说明: 保存邮箱并发送验证邮件 省市区地址查询 数据库建表 说明 页面静态化 注意 定时任务 安装 部分 详情页 异步任务的触发 。 后端接口设计 收货地址 使用缓存 安装 使用方法 为省市区视图添加缓存 数据库表设计 表结构 数据表结构 首页数据表结构 Docker使用 Docker简介 用户浏览历史记录 1. 保存 后端接口设计 后端实现 搜索 1. 需求分析 2. 搜索引擎原理 3. Elasticsearch 部分 业务需求分析 技术实现 数据存储设计 1. Redis保存已登录用户 商品部分 业务需求分析 技术实现 查询数据 1. 后端接口设计 部分 业务需求分析 技术实现 登录合并 修改登录视图 部分 保存 1. 后端接口设计 2. 后端实现 保存的思路 创建数据库模型类 接入 开发平台登录 沙箱环境 Xadmin 1. 安装 2. 使用 站点的全局配置 站点Model管理。 在Ubuntu中安装 2. 启动与停止 3. 镜像操作 端与自定义文件存储系统 1. 的Python客户端 安装 使用。
完整笔记资料代码:https://gitee.com/yinuo112/Backend/tree/master/Django/前后端分离django美多商城项目/note.md
感兴趣的小伙伴可以自取哦~
全套教程部分目录:
部分文件图片:
商品搜索
Haystack扩展建立索引
提示:
- [Elasticsearch]( 的底层是开源库 [Lucene]( Lucene,必须自己写代码去调用它的接口。
思考:
- 我们如何对接 Elasticsearch服务端?
解决方案:
- Haystack
1. Haystack介绍和安装配置
1.Haystack介绍
-
Haystack 是在Django中对接搜索引擎的框架,搭建了用户和搜索引擎之间的沟通桥梁。
-
我们在Django中可以通过使用 Haystack 来调用 Elasticsearch 搜索引擎。
-
Haystack 可以在不修改代码的情况下使用不同的搜索后端(比如
Elasticsearch
、Whoosh
、Solr
等等)。2.Haystack安装
$ pip install django-haystack
$ pip install elasticsearch==2.4.1
3.Haystack注册应用和路由
INSTALLED_APPS = ['haystack', # 全文检索
]
url(r'^search/', include('haystack.urls')),
4.Haystack配置
- 在配置文件中配置Haystack为搜索引擎后端
# HaystackHAYSTACK_CONNECTIONS = {'default': {'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine','URL': ' # Elasticsearch服务器ip地址,端口号固定为9200'INDEX_NAME': 'meiduo_mall', # Elasticsearch建立的索引库的名称},
}# 当添加、修改、删除数据时,自动生成索引HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
重要提示:
- HAYSTACK_SIGNAL_PROCESSOR 配置项保证了在Django运行起来后,有新的数据产生时,Haystack仍然可以让Elasticsearch实时生成新数据的索引
2. Haystack建立数据索引
1.创建索引类
- 通过创建索引类,来指明让搜索引擎对哪些字段建立索引,也就是可以通过哪些字段的关键字来检索数据。
- 本项目中对SKU信息进行全文检索,所以在
goods
应用中新建search_indexes.py
文件,用于存放索引类。
from haystack import indexesfrom .models import SKUclass SKUIndex(indexes.SearchIndex, indexes.Indexable):"""SKU索引数据模型类"""text = indexes.CharField(document=True, use_template=True)def get_model(self):"""返回建立索引的模型类"""return SKUdef index_queryset(self, using=None):"""返回要建立索引的数据查询集"""return self.get_model().objects.filter(is_launched=True)
-
索引类SKUIndex说明:
-
在
SKUIndex
建立的字段,都可以借助Haystack
由Elasticsearch
搜索引擎查询。 - 其中
text
字段我们声明为document=True
,表名该字段是主要进行关键字查询的字段。 text
字段的索引值可以由多个数据库模型类字段组成,具体由哪些模型类字段组成,我们用use_template=True
表示后续通过模板来指明。
2.创建
text
字段索引值模板文件
- 在
templates
目录中创建text字段
使用的模板文件 - 具体在
templates/search/indexes/goods/sku_text.txt
文件中定义
{{ object.id }}
{{ object.name }}
{{ object.caption }}
-
模板文件说明:当将关键词通过text参数名传递时
-
此模板指明SKU的
id
、name
、caption
作为text
字段的索引值来进行关键字索引查询。
3.手动生成初始索引
$ python manage.py rebuild_index
3. 全文检索测试
1.准备测试表单
- 请求方法:
GET
- 请求地址:
/search/
- 请求参数:
q
<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>
2.全文检索测试结果
结论:
- 错误提示告诉我们在
templates/search/
目录中缺少一个search.html
文件 search.html
文件作用就是接收和渲染全文检索的结果。
渲染商品搜索结果
1. 准备商品搜索结果页面
2. 渲染商品搜索结果
Haystack返回的数据包括:
query
:搜索关键字paginator
:分页paginator对象page
:当前页的page对象(遍历page
中的对象,可以得到result
对象)result.objects
: 当前遍历出来的SKU对象。
<div class="main_wrap clearfix"><div class=" clearfix"><ul class="goods_type_list clearfix">{% for result in page %}<li>{# object取得才是sku对象 #}<a href="/detail/{{ result.object.id }}/"><img src="{{ result.object.default_image.url }}"></a><h4><a href="/detail/{{ result.object.id }}/">{{ result.object.name }}</a></h4><div class="operate"><span class="price">¥{{ result.object.price }}</span><span>{{ result.object.comments }}评价</span></div></li>{% else %}<p>没有找到您要查询的商品。</p>{% endfor %}</ul><div class="pagenation"><div id="pagination" class="page"></div></div></div>
</div>
3. Haystack搜索结果分页
1.设置每页返回数据条数
- 通过
HAYSTACK_SEARCH_RESULTS_PER_PAGE
可以控制每页显示数量 - 每页显示五条数据:
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
2.准备搜索页分页器
<div class="main_wrap clearfix"><div class=" clearfix">......<div class="pagenation"><div id="pagination" class="page"></div></div></div>
</div>
<script type="text/javascript">$(function () {$('#pagination').pagination({currentPage: {{ page.number }},totalPage: {{ paginator.num_pages }},callback:function (current) {{#window.location.href = '/search/?q=iphone&page=1';#}window.location.href = '/search/?q={{ query }}&page=' + current;}})});
</script>
商品详情页
商品详情页分析和准备
1. 商品详情页组成结构分析
1.商品频道分类
-
已经提前封装在
contents.utils.py
文件中,直接调用方法即可。2.面包屑导航
-
已经提前封装在
goods.utils.py
文件中,直接调用方法即可。3.热销排行
-
该接口已经在商品列表页中实现完毕,前端直接调用接口即可。
4.商品SKU信息(详情信息)
-
通过
sku_id
可以找到SKU信息,然后渲染模板即可。 -
使用Ajax实现局部刷新效果。
5.SKU规格信息
-
通过
SKU
可以找到SPU规格和SKU规格信息。6.商品详情介绍、规格与包装、售后服务
-
通过
SKU
可以找到SPU
信息,SPU
中可以查询出商品详情介绍、规格与包装、售后服务。7.商品评价
-
商品评价需要在生成了订单,对订单商品进行评价后再实现,商品评价信息是动态数据。
- 使用Ajax实现局部刷新效果。
2. 商品详情页接口设计和定义
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /detail/(?P\d+)/ |
> | |
2.请求参数:路径参数 |
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
sku_id | string | 是 | 商品SKU编号 |
> | |||
3.响应结果:HTML |
detail.html
4.接口定义
class DetailView(View):"""商品详情页"""def get(self, request, sku_id):"""提供商品详情页"""return render(request, 'detail.html')
3. 商品详情页初步渲染
渲染商品频道分类、面包屑导航、商品热销排行
- 将原先在商品列表页实现的代码拷贝到商品详情页即可。
- 添加
detail.js
class DetailView(View):"""商品详情页"""def get(self, request, sku_id):"""提供商品详情页"""# 获取当前sku的信息try:sku = models.SKU.objects.get(id=sku_id)except models.SKU.DoesNotExist:return render(request, '404.html')# 查询商品频道分类categories = get_categories()# 查询面包屑导航breadcrumb = get_breadcrumb(sku.category)# 渲染页面context = {'categories':categories,'breadcrumb':breadcrumb,'sku':sku,}return render(request, 'detail.html', context)
提示:为了让前端在获取商品热销排行数据时,能够拿到商品分类ID,我们将商品分类ID从模板传入到Vue.js
<script type="text/javascript">let category_id = "{{ sku.category.id }}";
</script>
data: {category_id: category_id,
},
展示详情页数据
1. 查询和渲染SKU详情信息
# 渲染页面context = {'categories':categories,'breadcrumb':breadcrumb,'sku':sku,
}
return render(request, 'detail.html', context)
<div class="goods_detail_con clearfix"><div class="goods_detail_pic fl"><img src="{{ sku.default_image.url }}"></div><div class="goods_detail_list fr"><h3>{{ sku.name }}</h3><p>{{ sku.caption }}</p><div class="price_bar"><span class="show_pirce">¥<em>{{ sku.price }}</em></span><a href="javascript:;" class="goods_judge">18人评价</a></div><div class="goods_num clearfix"><div class="num_name fl">数 量:</div><div class="num_add fl"><input v-model="sku_count" @blur="check_sku_count" type="text" class="num_show fl"><a @click="on_addition" class="add fr">+</a><a @click="on_minus" class="minus fr">-</a></div> </div>{#...商品规格...#}<div class="total" v-cloak>总价:<em>[[ sku_amount ]]元</em></div><div class="operate_btn"><a href="javascript:;" class="add_cart" id="add_cart">加入购物车</a> </div></div>
</div>
提示:为了实现用户选择商品数量的局部刷新效果,我们将商品单价从模板传入到Vue.js
<script type="text/javascript">let sku_price = "{{ sku.price }}";
</script>
data: {sku_price: sku_price,
},
2. 查询和渲染SKU规格信息
1.查询SKU规格信息
class DetailView(View):"""商品详情页"""def get(self, request, sku_id):"""提供商品详情页"""# 获取当前sku的信息try:sku = models.SKU.objects.get(id=sku_id)except models.SKU.DoesNotExist:return render(request, '404.html')# 查询商品频道分类categories = get_categories()# 查询面包屑导航breadcrumb = get_breadcrumb(sku.category)# 构建当前商品的规格键sku_specs = sku.specs.order_by('spec_id')sku_key = []for spec in sku_specs:sku_key.append(spec.option.id)# 获取当前商品的所有SKUskus = sku.spu.sku_set.all()# 构建不同规格参数(选项)的sku字典spec_sku_map = {}for s in skus:# 获取sku的规格参数s_specs = s.specs.order_by('spec_id')# 用于形成规格参数-sku字典的键key = []for spec in s_specs:key.append(spec.option.id)# 向规格参数-sku字典添加记录spec_sku_map[tuple(key)] = s.id# 获取当前商品的规格信息goods_specs = sku.spu.specs.order_by('id')# 若当前sku的规格信息不完整,则不再继续if len(sku_key) < len(goods_specs):returnfor index, spec in enumerate(goods_specs):# 复制当前sku的规格键key = sku_key[:]# 该规格的选项spec_options = spec.options.all()for option in spec_options:# 在规格参数sku字典中查找符合当前规格的skukey[index] = option.idoption.sku_id = spec_sku_map.get(tuple(key))spec.spec_options = spec_options# 渲染页面context = {'categories':categories,'breadcrumb':breadcrumb,'sku':sku,'specs': goods_specs,}return render(request, 'detail.html', context)
2.渲染SKU规格信息
{% for spec in specs %}
<div class="type_select"><label>{{ spec.name }}:</label>{% for option in spec.spec_options %}{% if option.sku_id == sku.id %}<a href="javascript:;" class="select">{{ option.value }}</a>{% elif option.sku_id %}<a href="{{ url('goods:detail', args=(option.sku_id, )) }}">{{ option.value }}</a>{% else %}<a href="javascript:;">{{ option.value }}</a>{% endif %}{% endfor %}
</div>
{% endfor %}
3. 查询和渲染详情、包装和售后信息
商品详情、包装和售后信息被归类到商品SPU中,
sku.spu
关联查询就可以找到该SKU
的SPU
信息。
<div class="r_wrap fr clearfix"><ul class="detail_tab clearfix"><li @click="on_tab_content('detail')" :class="tab_content.detail?'active':''">商品详情</li><li @click="on_tab_content('pack')" :class="tab_content.pack?'active':''">规格与包装</li><li @click="on_tab_content('service')" :class="tab_content.service?'active':''">售后服务</li><li @click="on_tab_content('comment')" :class="tab_content.comment?'active':''">商品评价(18)</li></ul><div @click="on_tab_content('detail')" class="tab_content" :class="tab_content.detail?'current':''"><dl><dt>商品详情:</dt><dd>{{ sku.spu.desc_detail|safe }}</dd></dl></div><div @click="on_tab_content('pack')" class="tab_content" :class="tab_content.pack?'current':''"><dl><dt>规格与包装:</dt><dd>{{ sku.spu.desc_pack|safe }}</dd></dl></div><div @click="on_tab_content('service')" class="tab_content" :class="tab_content.service?'current':''"><dl><dt>售后服务:</dt><dd>{{ sku.spu.desc_service|safe }}</dd></dl></div><div @click="on_tab_content('comment')" class="tab_content" :class="tab_content.comment?'current':''"><ul class="judge_list_con">{#...商品评价...#}</ul></div>
</div>