每日分享
I would rather die of passion than of boredom.
我宁愿死于激情而不是无聊。
小闫语录:
赵鑫珊在《哲学与当代世界》中曾说过这样一句话『自古至今,人类最伟大的精神产品(科学的,艺术的和哲学的),无一不是出自一腔激情』。激情是一种积极的态度,它会激发我们的潜能;激情是一种神奇的药剂,它会使我们周围的人被感染;激情是成功的必备因素,它会使我们把事情做至极致。有人充满激情奋斗终身,也有人自甘平凡,碌碌无为,得过且过。人生需要激情,年轻人更需要激情,否则在社会进步的浪潮里终有一天会被淘汰。
不要戴着『佛系』的帽子,终其一生。你要承认自己的怯懦,承认自己的逃避,不要为自己找任何托词,你只是在掩耳盗铃罢了。你敢说你不羡慕那些物质优渥的人吗?你敢说你不想超越身边的某个人吗?你只是怕努力之后没有得到想要的,你只是怕最后的结果是你比不过某个人。然后就谎称你不想、谎称不屑于去做,自居清高,实则是懦弱,逃避。充满激情,认真做好每一件事,重要的不是结果,而是过程。不要攀比,不要嘲笑某人,因为每个人都有其他人无法比的一些闪光点。
无聊的一生平平淡淡,难道你到了这个世上只是为了呼吸一下空气,看看其他人的辉煌美好吗?充满激情的度过每一天,有可能是大起大落,有可能是一路惊喜不断,但是为了想要的生活不断的努力,活成别人羡慕的模样不好吗?
美多商城项目(五)
1.typroa中画流程图
1.1横向流程图
注意:横向的流程图,代码块中首行标明graph LR,代码块标明语言是mermaid
代码示例:
```mermaid graph LR A[方形]-->B(圆角) B-->C{条件a} C-->|a=1|D[结果1] C-->|a=2|E[结果2] F[横向流程图] ```
图表示例:
1.2纵向流程图
注意:纵向的流程图,代码块中首行标明graph TD,代码块标明语言是mermaid
代码示例:
```mermaid graph TD A[方形]-->B(圆角) B-->C{条件a} C-->|a=1|D[结果1] C-->|a=2|E[结果2] F[纵向流程图] ```
图表示例:
2.数据缓存
2.1网站性能优化-数据缓存
前面的文章中我们写过这样一个接口:
GET /areas/:获取所有省级地区的信息
假如1分钟之内有500个用户访问了GET /areas/,服务器就需要查询500次数据库,但是最终每个用户获取到的结果是一样的。为了减少数据库的查询次数,提高效率,让用户体验度上升,我们可以使用数据缓存。
数据缓存:把经常被用户访问的数据放到缓存(redis)中,当用户来访问时,直接从缓存中获取数据进行返回,只有缓存中不存在时才查询数据库。
访问流程
客户端向服务器访问数据的时候,服务器先到redis缓存中获取对应的数据,如果获取到数据,直接进行返回;如果获取不到数据,再去查询数据库。并且在查询出数据,返回结果之前,先将查询的结果存到缓存中,便于下次使用。
2.2使用缓存
在Django REST framework中使用缓存,可以通过 drf-extensions
扩展来实现。
2.2.1安装
pip install drf-extensions
2.2.2使用方法
直接添加装饰器
可以在使用restframeworkextensions.cache.decorators中的cache_response装饰器来装饰返回数据的类视图的对象方法,如
class CityView(views.APIView): @cache_response() def get(self, request, *args, **kwargs): ...
装饰器的作用:访问接口的时候,会先到缓存中进行获取,没有的话才会执行接口函数。
cache_response装饰器可以接收两个参数
@cache_response(timeout=60*60, cache='default')
timeout 缓存时间
cache 缓存使用的Django缓存后端(即CACHES配置中的键名称)
如果在使用cache_response装饰器时未指明timeout或者cache参数,则会使用配置文件中的默认配置,可以通过如下方法指明:
# DRF扩展 REST_FRAMEWORK_EXTENSIONS = { # 缓存时间 'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60, # 缓存存储 'DEFAULT_USE_CACHE': 'default', }
DEFAULTCACHERESPONSE_TIMEOUT 缓存有效期,单位秒
DEFAULTUSECACHE 缓存的存储方式,与配置文件中的
CACHES
的键对应。
注意,cache_response装饰器既可以装饰在类视图中的get方法上,也可以装饰在REST framework扩展类提供的list或retrieve方法上。使用cacheresponse装饰器无需使用method_decorator进行转换。
使用drf-extensions提供的扩展类
drf-extensions扩展对于缓存提供了三个扩展类:
ListCacheResponseMixin
用于缓存返回列表数据的视图,与ListModelMixin扩展类配合使用,实际是为list方法添加了cache_response装饰器
RetrieveCacheResponseMixin
用于缓存返回单一数据的视图,与RetrieveModelMixin扩展类配合使用,实际是为retrieve方法添加了cache_response装饰器
CacheResponseMixin
为视图集同时补充List和Retrieve两种缓存,与ListModelMixin和RetrieveModelMixin一起配合使用。
三个扩展类都是在 rest_framework_extensions.cache.mixins
中。
2.2.3为省市区视图添加缓存
因为省市区视图使用了视图集,并且视图集中有提供ListModelMixin和RetrieveModelMixin的扩展(由ReadOnlyModelViewSet提供),所以可以直接添加CacheResponseMixin扩展类。
修改返回省市区信息的视图
from rest_framework_extensions.cache.mixins import CacheResponseMixin class AreasViewSet(CacheResponseMixin,ReadOnlyModelViewSet): """ 行政区划信息 """ pagination_class = None # 区划信息不分页 def get_queryset(self): """ 提供数据集 """ if self.action == 'list': return Area.objects.filter(parent=None) else: return Area.objects.all() def get_serializer_class(self): """ 提供序列化器 """ if self.action == 'list': return AreaSerializer else: return SubAreaSerializer
2.2.4缓存数据保存位置与有效期设置
我们想把缓存数据保存在redis中,且设置有效期,可以通过在配置文件中定义的方式来实现。
在配置文件中增加
# DRF扩展 REST_FRAMEWORK_EXTENSIONS = { # 缓存时间 'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60, # 缓存存储 'DEFAULT_USE_CACHE': 'default', }
3.用户地址
用户在添加收货地址的时候,我们需要将用户的地址进行保存,因此需要先创建一个模型类,然后迁移生成表。数据库有用户地址表后,我们就可以将用户地址保存到数据库了。
数据库的表格一般都是DBA进行设计的,我们不需要深入了解,所以此处不做过多的阐述。
在用户模型类中有一个小点回顾一下:
ordering 表示的是表名在进行Address查询时,默认使用的排序方式。默认是升序,如果想改为降序,只需要在前面添加一个减号 -
。
ordering = ['-update_time']
其中updatetime代表的是按照updatetime进行排序。
3.1设置默认地址
可以在用户地址模型类中添加一个标记 is_default
,如果是默认地址,将标记改为True。但是这种方法比较麻烦,修改需要两步,先将原来的默认地址标记改为False,再将要设置默认地址的标记改为True。我们可以换一种方法:在用户表中添加一个字段。
用户表
ID(用户ID) | ... | DEFAULT_ADDRESS_ID(默认地址ID) |
1 | ... | 2 |
在用户表中,我们只需要修改默认地址id。例如我想更改用户A的默认地址为B,直接将默认地址id更改B的id就可以了,只需一步,比上一种方法要好的多。
设置步骤
为User模型类添加默认地址
class User(AbstractUser): ... default_address = models.ForeignKey('Address', related_name='users', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='默认地址') ...
3.2地址管理
3.2.1登录用户地址新增
API: POST /addresses/ 参数: 通过请求头传递jwt token { "title":"地址标题", "receiver":"收货人", "province_id":"省id", "city_id":"市id", "district_id":"区id", "place":"详细地址", "mobile":"手机号", "tel":"电话", # 可以不传递 "email":"邮箱" # 可以不传递 } 响应: { "id":"地址id", "title":"地址标题", "receiver":"收货人", "province":"省名称", "city":"市名称", "district":"区县名称", "province_id":"省id", "city_id":"市id", "district_id":"区id", "place":"详细地址", "mobile":"手机号", "tel":"电话", # 可以不传递 "email":"邮箱" # 可以不传递 }
因为用户界面显示的是省市县的名称,所以我们响应数据不能传递id,因此需要添加三个数据,将名称返回。
业务逻辑:
1.先设置用户权限,只有认证用户才可以对此接口进行访问。
2.判断用户的地址数量是否超过上限。
3.获取参数并进行校验(参数完整性,手机号格式,邮箱格式)。
4.创建并保存新增地址数据。
5.将新增地址数据序列化并返回。
写代码之前,先定义地址的序列化器类。
因为我们需要的字段有点多,我们可以不用field指定字段,而是使用exclude排除我们不需要的几个字段即可。
对于没有的字段provinceid、cityid和district_id,我们需要自己定义。
有些字段是序列化时使用,有些字段是反序列化时使用,因此我们需要对这些字段通过参数进行设置。
因为我们序列化时需要的是省市县的名称,所以我们在嵌套序列化的时候使用StringRelatedField方法。系统自动生成的时候,默认是序列化为主键,我们需要对其进行更改。
我们可以在序列化器类中定义validate_mobile方法来校验手机号。
ModelSerializer中的create不适用,因为我们新增的数据中没有user,但是用户表中是有这个字段的,而且是必填项,所以我们需要重写create方法,将user添加进去再调用系统的create方法将数据保存。
a.创建并保存新增地址数据。
b.获取登录用户对象。
除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
serializer =AccountSerializer(account, context={'request': request})
通过context参数附加的数据,可以通过Serializer对象的context属性获取。
self.get_serializer(...)创建序列化器对象时,会向序列化器对象的context属性中补充request参数,可以通过 序列化器对象.context[
'request']
来获取request对象。
c.调用ModelSerializer中create方法。
3.2.2获取登录用户地址数据
API: GET /addresses/ 参数: 通过请求头传递jwt token 响应: { "user_id":"用户id", "default_address_id":"用户默认地址id", "limit":"用户地址最大数量", "addresses":"用户地址" }
业务逻辑:
1.获取登录用户所有地址数据。
2.将地址数据序列化并返回。
3.2.3删除(逻辑删除)用户指定地址
API: DELETE /addresses/(?P<pk>\d+)/ 参数: 通过url地址传递地址的pk 通过请求头传递jwt token 响应: 状态码204
业务逻辑:
1.根据pk获取指定的地址数据。
2.将地址的is_deleted设置为True。
3.返回应答。
3.2.4修改登录用户指定地址
API: PUT /addresses/(?P<pk>\d+)/ 参数: 通过url地址传递地址的pk 通过请求头传递jwt token { "title":"地址标题", "receiver":"收货人", "province_id":"省id", "city_id":"市id", "district_id":"区id", "place":"详细地址", "mobile":"手机号", } 响应: { "id":"地址id", "title":"地址标题", "receiver":"收货人", "province":"省名称", "city":"市名称", "district":"区县名称", "province_id":"省id", "city_id":"市id", "district_id":"区id", "place":"详细地址", "mobile":"手机号", "tel":"电话", # 可以不传递 "email":"邮箱" # 可以不传递 }
业务逻辑:
1.根据pk获取指定的地址数据。
2.获取参数并进行校验。
3.修改指定地址的数据。
4.返回修改地址序列化数据。
UpdateModelMixin中已经实现了update方法,而且满足我们的需求,所以我们可以不写这个方法。
3.2.5设置登录用户默认地址
API: PUT /addresses/(?P<pk>\d+)/status/ 参数: 通过url地址传递地址的pk 通过请求头传递jwt token 响应: { "message":"OK" }
业务逻辑:
1.根据pk查询指定的地址。
2.设置登录用户的默认地址。
3.返回应答,设置成功。
3.2.6修改登录用户指定地址标题
API: PUT /addresses/(?P<pk>\d+)/title/ 参数: 通过url传递地址的pk 通过请求头传递jwt token 通过请求体参数title 响应: { "id":"地址id", "title":"地址标题" }
业务逻辑:
1.根据pk查询指定的地址。
2.获取title参数并校验(title必传)。
3.修改指定地址的标题并更新数据库。
4.返回应答,设置标题成功。
4.商品部分
4.1商品部分用户表设计
使用工具『StarUML』
首页广告数据表结构:
商品数据表结构:
4.2商品数据存储
SPU:属性值,特性相同的商品统称。例如:iPhoneX
SKU:涉及到某个具体规格的产品。例如:iPhoneX 红色 256G 全网通
MacBook Pro --->SPU
MacBook Pro 银色 512G 15.4寸---->SKU
4.3FDFS文件存储系统
淘宝架构师:余庆
七牛云:文件存储系统,花钱就能用,不需要自己进行维护。(小型公司使用居多)
FDFS:文件存储系统,自己搭建,自己进行维护。(大型公司一般自己搭建维护)
4.3.1概念
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制, 充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
冗余处理:不同的人保存的相同的内容,可以只保存一份,节省空间。
备份:防止数据丢失。
负载均衡:多个服务器共同处理,提高效率。
线性扩容:方便后期扩容。
4.3.2架构
Tracker server:调度服务器,负责负载均衡和调度。
Storage server:存储服务器,负责进行文件的存储。不止一个。
4.3.3详细介绍
详细介绍请查看『我是个链接』一文。
4.4Docker
Docker 可以让开发者打包他们的应用以及依赖环境,将打包好的文件上传到其他Linux电脑上,使用docker加载之后可以直接进行使用。
4.4.1镜像(image)
打包的应用以及依赖包构成了一个docker镜像
镜像操作命令:
查看本地docker中有哪些镜像
docker image ls
拉取镜像到本地docker
docker image pull ...
删除docker镜像
docker image rm 镜像名/镜像ID
4.4.2容器(container)
把镜像运行起来之后变成了一个容器。
4.4.3C/S模型
Docker客户端只需向Docker服务器或者守护进程发出请求,服务器或者守护进程将完成所有工作并且返回结果。
4.4.4Registry(注册中心)
Docker 用 Registry 来保存用户构建的镜像。Registry 分为公共和私有两种。
4.4.5使用
详细安装使用步骤请查看『我是个链接』一文。
总结回顾
1.视图集对象action属性使用场景
重写getserializerclass和get_queryset,根据不同的action操作,返回不同的序列化器和不同的查询集。
2.网站性能优化-数据缓存
扩展:drf-extensions
cache_response装饰器:进行数据缓存设置和获取。
3.地址管理-地址新增
self.get_serializer(...)
上面的代码在创建序列化器对象的时候,会向序列化器对象的context属性中补充request参数。
4.商品数据存储
商品存储数据表设计。
SPU:属性值,特性相同的商品统称。例如iPhoneX
SKU:涉及到某个具体规格的产品。例如:iPhoneX 红色 256G 全网通
5.FDFS文件存储系统
概念和架构看一下,做一个了解。
传文件的内部过程。
6.Docker
将应用的依赖环境进行打包,将打包好的文件上传到其他Linux电脑上,使用docker加载之后可以直接进行使用。
镜像的操作命令再看一下。