昨日内容回顾

  1. 1. django请求生命周期?
  2. - 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端
  3. 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中.
  4. - url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
  5. 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
  6. - 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
  7. - 客户端浏览器接收到返回的数据,经过渲染后显示给用户.
  8. 1. django请求生命周期?
  9. - 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端
  10. 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中.
  11. - url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
  12. 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
  13. - 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
  14. - 客户端浏览器接收到返回的数据,经过渲染后显示给用户.
  15. 2. django提供的功能
  16. - 必备
  17. - 路由
  18. - 视图
  19. - 模板渲染
  20. - django
  21. - ORM
  22. ...
  23. ...
  24. - 分页
  25. - Form & ModelForm
  26. - admin
  27. - auth
  28. - session
  29. - 中间件
  30. - contenttype
  31. - csrf
  32. - 缓存(速度块)
  33. 3. restful
  34. - restful 规范
  35. - django rest framwork
  36. - 其他
  37. - 跨域
  38. a. 为什么出现跨域?
  39. b. 如何解决跨域?
  40. 使用cors,即:设置响应头。
  41. 简单请求:
  42. 响应头中设置一个允许域名访问
  43. 复杂请求:
  44. OPTIONS请求做预检,允许特殊请求方式和请求头 + 允许域名访问。
  45. 真正请求就可以发送过来进行处理 + 允许域名访问。
  46. c. 跨域
  47. www.baidu.com / www.luffycity.com
  48. www.baidu.com / api.luffycity.com
  49. www.baidu.com:8001 / www.baidu.com:8002
  50. d. 路飞线上代码无跨域(项目部署时,放在同一处)
  51. - vue.js
  52. - 前端三大框架:react.js /angular.js / vue.js
  53. - vue.js 2版本
  54. - 组件:
  55. - axios
  56. - vuex
  57. - router
  58. - 你觉得vuejQuery的区别?
  59. - 双向绑定(数据变动,页面也随之更改)
  60. - 单页面应用(切换页面,页面不刷新)

一、redis使用

redis介绍

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它的数据,存在内存中,读写速度快!也可以做持久化。

redis安装

使用centos系统安装

  1. yum install -y redis

redis使用

注意:redis是安装在linux系统里面的,但是python程序是运行在windows系统中的。所以需要进行远程连接!

但是,redis默认使用127.0.0.1连接,端口为6379

修改配置

编辑配置文件

  1. vim /etc/redis.conf

修改IP,关闭保护模式(否则无法远程操作redis)

  1. bind 192.168.218.133
  2. protected-mode no

启动redis

注意:必须指定配置文件

  1. redis-server /etc/redis.conf

注意,此时终端不会有输出,再开一个窗口,查看端口

  1. netstat -anpt

信息如下:

  1. tcp 0 0 192.168.218.133:6379 0.0.0.0:* LISTEN 3736/redis-server 1
  2. tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 995/sshd
  3. tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2275/master
  4. tcp 0 0 192.168.218.133:22 192.168.218.1:59646 ESTABLISHED 2575/sshd: root@not
  5. tcp 0 0 192.168.218.133:22 192.168.218.1:58928 ESTABLISHED 2500/sshd: root@pts
  6. tcp 0 0 192.168.218.133:22 192.168.218.1:55251 ESTABLISHED 3739/sshd: root@pts
  7. tcp6 0 0 :::3306 :::* LISTEN 2220/mysqld
  8. tcp6 0 0 :::22 :::* LISTEN 995/sshd
  9. tcp6 0 0 ::1:25 :::* LISTEN 2275/master

第一个就是redis,端口为6379

注意要关闭防火墙

  1. /etc/init.d/iptables stop

redis相当于是一个在内存中的创建的大字典

redis的value有5大数据类型:字符串,哈希,列表,集合,有序集合

字符串(String)

Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下:

  1. COMMAND KEY_NAME

set()

get(name)

分别表示设置和获取

举例:

写一个字符串,并获取

  1. import redis
  2. conn = redis.Redis(host='192.168.218.133',port='6379')
  3. conn.set('name','xiao') # 写入字符串
  4. val = conn.get('name') # 获取字符串
  5. print(val)

执行程序,输出如下:

  1. b'xiao'
  2. xiao

注意:它的返回结果是bytes,那么使用decode(‘utf-8’)解码之后,就会变成字符串

哈希(Hash)

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。

Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)

redis中的Hash 在内存中类似于一个name对应一个dic来存储

hset(name, key, value)

name对应的hash中设置一个键值对(不存在,则创建,否则,修改)

hget(name,key)

在name对应的hash中根据key获取value

举例:

  1. import redis
  2. conn = redis.Redis(host='192.168.218.133',port='6379')
  3. conn.hset("dic_name","a1","aa") # 写入字典
  4. val = conn.hget("dic_name","a1") # 获取key为a1的值
  5. print(val)
  6. print(val.decode('utf-8')) # 解码

执行输出:

  1. b'aa'
  2. aa

hgetall(name)

获取name对应hash的所有键值

举例:

  1. import redis
  2. conn = redis.Redis(host='192.168.218.133',port='6379')
  3. val = conn.hgetall("dic_name") # 获取dic_name的所有值
  4. print(val)

执行输出:

  1. {b'a1': b'aa'}

hmset(name, mapping)

在name对应的hash中批量设置键值对,mapping:字典

hmget(name, keys, *args)

在name对应的hash中获取多个key的值

举例:

  1. import redis
  2. conn = redis.Redis(host='192.168.218.133',port='6379')
  3. dic={"a1":"aa","b1":"bb"} # 定义一个字典
  4. conn.hmset("dic_name",dic) # 批量设置键值对
  5. val_1 = conn.hget("dic_name","b1") # 获取key为b1的值
  6. val_2 = conn.hmget("dic_name","a1","b1") # 获取多个值
  7. print(val_1)
  8. print(val_1.decode('utf-8')) # 解码
  9. print(val_2)

执行输出:

  1. b'bb'
  2. bb
  3. [b'aa', b'bb']

列表(List)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

redis中的List在在内存中按照一个name对应一个List来存储

lpush(name,values)

llen(name)

lindex(name, index)

举例:

  1. import redis
  2. conn = redis.Redis(host='192.168.142.129',port='6379')
  3. conn.lpush("list_name",2)# list_name中增加一个值2
  4. print(conn.llen("list_name")) # 获取列表元素的个数
  5. val = conn.lindex("list_name",0) #根据索引获取列表内元素
  6. print(val)

执行输出:

  1. 1
  2. b'2'

集合(Set)

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

sadd(name,values)

smembers(name)

scard(name)

举例

  1. import redis
  2. conn = redis.Redis(host='192.168.142.129',port='6379')
  3. conn.sadd("set_name","aa") # 在集合set_name中增加元素
  4. conn.sadd("set_name","aa","bb")
  5. print(conn.smembers("set_name")) # 获取set_name集合的所有成员
  6. val = conn.scard("set_name") #获取set_name集合中的元素个数
  7. print(val)

执行输出:

  1. {b'bb', b'aa'}
  2. 2

有序集合(sorted set)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

zadd(name, args, *kwargs)

zcard(name)

zcount(name, min, max)

举例:

  1. import redis
  2. conn = redis.Redis(host='192.168.142.129',port='6379')
  3. conn.zadd("zset_name", "a1", 6, "a2", 2,"a3",5) # 在有序集合zset_name中增加元素
  4. # 或者使用下面的方式,效果同上!
  5. # conn.zadd('zset_name1', b1=10, b2=5)
  6. print(conn.zcard("zset_name")) # 获取有序集合内元素的数量
  7. val = conn.zcount("zset_name",1,5) #获取有序集合中分数在[min,max]之间的个数
  8. print(val)

执行输出:

  1. 3
  2. 2

总结:

  1. a. 五大数据类型:
  2. 字符串,哈希,列表,集合,有序集合
  3. b. 列举每种数据类型的操作
  4. 字符串:
  5. set
  6. get
  7. 字典:
  8. hget
  9. hgetall
  10. hset
  11. hmset
  12. hdel
  13. 其他:
  14. delete
  15. expire
  16. keys
  17. flushall()

更多redis操作,请参考以下文章

http://www.runoob.com/redis/redis-lists.html

http://www.cnblogs.com/melonjiang/p/5342505.html

二、购物车

下载代码:

https://github.com/987334176/luffycity/archive/v1.3.zip

下载数据库使用(务必下载,上面的压缩包数据库是空的!!!)

https://github.com/987334176/luffycity/blob/master/db.sqlite3

进入api目录,务必删除views.py,它已经没有用了

先来看一个购物车步骤

  1. 1. 接受用户选中的课程ID和价格策略ID
  2. 2. 判断合法性
  3. - 课程是否存在?
  4. - 价格策略是否合法?
  5. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR

修改api_urls.py

  1. from django.conf.urls import url
  2. from api.views import course,degreecourse,auth,shoppingcart
  3. urlpatterns = [
  4. url(r'auth/$', auth.AuthView.as_view({'post':'login'})),
  5. url(r'courses/$',course.CoursesView.as_view()),
  6. url(r'courses/(?P<pk>\d+)/$',course.CourseDetailView.as_view()),
  7. url(r'shoppingcart/$', shoppingcart.ShoppingCartView.as_view({'get':'list','post':'create'})),
  8. ]

修改views目录下的shoppingcart.py

  1. from rest_framework.views import APIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from rest_framework.response import Response
  4. from api import models
  5. class ShoppingCartView(ViewSetMixin,APIView):
  6. def list(self, request, *args, **kwargs):
  7. """
  8. 查看购物车信息
  9. :param request:
  10. :param args:
  11. :param kwargs:
  12. :return:
  13. """
  14. return Response('ok')
  15. def create(self,request,*args,**kwargs):
  16. """
  17. 加入购物车
  18. :param request:
  19. :param args:
  20. :param kwargs:
  21. :return:
  22. """
  23. """
  24. 1. 接受用户选中的课程ID和价格策略ID
  25. 2. 判断合法性
  26. - 课程是否存在?
  27. - 价格策略是否合法?
  28. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  29. 注意:用户ID=1
  30. """
  31. # 1. 接受用户选中的课程ID和价格策略ID
  32. print('要加入购物车了')
  33. print(request.body,type(request.body))
  34. print(request.data, type(request.data))
  35. return Response({'code':1000})

使用postman发送json数据

Day101 redis操作,购物车,DRF解析器 - 图1

查看返回信息

Day101 redis操作,购物车,DRF解析器 - 图2

查看Pycharm控制台输出:

  1. 要加入购物车了
  2. b'{"courseid":"1","policyid":"2"}' <class 'bytes'>
  3. {'courseid': '1', 'policyid': '2'} <class 'dict'>

可以发现body的数据是bytes类型的。那么request.data的数据,怎么就成字典了呢?

假设抛开request.data。使用request.body的数据,解析成字典。需要经历2个步骤:

  1. 将数据使用decode(‘utf-8’),进行解码得到字符串
  2. 将字符串使用json.load(‘value’),反序列化成字典。

那么rest framework就自动帮你做了这件事情!详情看下面的内容。

三、DRF解析器

1.Parser对象

REST框架提供了一系列的内建Parser对象来对不同的媒体类型进行解析,也支持为API接口灵活的自定义Parser

如何选择合适的Parser

通常为一个viewset定义一个用于解析的Parser对象列表

当接收到request.data时,REST框架首先检查请求头的Content-Type字段,然后决定使用哪种解析器来处理请求内容

注意:

当你编写客户端应用程序时,发送HTTP请求时,一定要在请求头中设置Content-Type。

如果你没有设置这个属性,大多数客户端默认使用’application/x-www-form-urlencoded’,但这有时并不是你想要的。

例如当你用jQuery的ajax方法发送一个json编码的数据时,应该确保包含contentType: ‘application/json’设置。

设置默认的解析器

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_PARSER_CLASSES': (
  3. 'rest_framework.parsers.JSONParser',
  4. )
  5. }

也可以为基于APIView的单个视图类或者视图集合设置自己的Parser

  1. from rest_framework.parsers import JSONParser
  2. from rest_framework.response import Response
  3. from rest_framework.views import APIView
  4. class ExampleView(APIView):
  5. """
  6. 一个能处理post提交的json数据的视图类
  7. """
  8. parser_classes = (JSONParser,)
  9. def post(self, request, format=None):
  10. return Response({'received data': request.data})

使用装饰器的视图函数:

  1. from rest_framework.decorators import api_view
  2. from rest_framework.decorators import parser_classes
  3. # 注意装饰器顺序
  4. @api_view(['POST'])
  5. @parser_classes((JSONParser,))
  6. def example_view(request, format=None):
  7. """
  8. A view that can accept POST requests with JSON content.
  9. """
  10. return Response({'received data': request.data})

举例:

修改views目录下的shoppingcart.py,使用解析器

JSONParser对应的数据类型为application/json

FormParser对应的数据类型为application/x-www-form-urlencoded

  1. from rest_framework.views import APIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from rest_framework.response import Response
  4. from api import models
  5. from rest_framework.parsers import JSONParser,FormParser
  6. class ShoppingCartView(ViewSetMixin,APIView):
  7. parser_classes = [JSONParser,FormParser] # 指定解析器
  8. def list(self, request, *args, **kwargs):
  9. """
  10. 查看购物车信息
  11. :param request:
  12. :param args:
  13. :param kwargs:
  14. :return:
  15. """
  16. return Response('ok')
  17. def create(self,request,*args,**kwargs):
  18. """
  19. 加入购物车
  20. :param request:
  21. :param args:
  22. :param kwargs:
  23. :return:
  24. """
  25. """
  26. 1. 接受用户选中的课程ID和价格策略ID
  27. 2. 判断合法性
  28. - 课程是否存在?
  29. - 价格策略是否合法?
  30. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  31. 注意:用户ID=1
  32. """
  33. # 1. 接受用户选中的课程ID和价格策略ID
  34. print('要加入购物车了')
  35. # print(request.body,type(request.body))
  36. print(request._request, type(request._request)) # 原生django的request
  37. print(request._request.body) # 获取body
  38. print(request._request.POST) # 获取post
  39. print(request.data, type(request.data)) # 封装后的数据
  40. return Response({'code':1000})

使用postman再次发送,查看Pycharm控制台输出:

  1. <WSGIRequest: POST '/api/v1/shoppingcart/'> <class 'django.core.handlers.wsgi.WSGIRequest'>
  2. b'{"courseid":"1","policyid":"2"}'
  3. <QueryDict: {}>
  4. {'policyid': '2', 'courseid': '1'} <class 'dict'>

从上面的信息中,可以看出。原生的django通过body可以获取数据,但是post的数据是空的。因为客户端的请求数据类型不是

application/x-www-form-urlencoded

而经过rest framework封装之后,可以从data中获取数据,并解析成字典了!

查看APIView源码

  1. class APIView(View):
  2. # The following policies may be set at either globally, or per-view.
  3. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
  4. parser_classes = api_settings.DEFAULT_PARSER_CLASSES
  5. authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
  6. throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
  7. permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
  8. content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
  9. metadata_class = api_settings.DEFAULT_METADATA_CLASS
  10. versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

看这一句,它默认会从settings.py中查找解析器

  1. parser_classes = api_settings.DEFAULT_PARSER_CLASSES

如果需要指定默认的解析器,修改settings.py

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
  3. 'VERSION_PARAM':'version',
  4. 'DEFAULT_VERSION':'v1',
  5. 'ALLOWED_VERSIONS':['v1','v2'],
  6. 'PAGE_SIZE':20,
  7. 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
  8. 'DEFAULT_PARSER_CLASSES': (
  9. 'rest_framework.parsers.JSONParser',
  10. )
  11. }

修改views目录下的shoppingcart.py,注释掉解析器

  1. from rest_framework.views import APIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from rest_framework.response import Response
  4. from api import models
  5. # from rest_framework.parsers import JSONParser,FormParser
  6. class ShoppingCartView(ViewSetMixin,APIView):
  7. # parser_classes = [JSONParser,FormParser] # 指定解析器
  8. def list(self, request, *args, **kwargs):
  9. """
  10. 查看购物车信息
  11. :param request:
  12. :param args:
  13. :param kwargs:
  14. :return:
  15. """
  16. return Response('ok')
  17. def create(self,request,*args,**kwargs):
  18. """
  19. 加入购物车
  20. :param request:
  21. :param args:
  22. :param kwargs:
  23. :return:
  24. """
  25. """
  26. 1. 接受用户选中的课程ID和价格策略ID
  27. 2. 判断合法性
  28. - 课程是否存在?
  29. - 价格策略是否合法?
  30. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  31. 注意:用户ID=1
  32. """
  33. # 1. 接受用户选中的课程ID和价格策略ID
  34. print('要加入购物车了')
  35. # print(request.body,type(request.body))
  36. print(request._request, type(request._request)) # 原生django的request
  37. print(request._request.body) # 获取body
  38. print(request._request.POST) # 获取post
  39. print(request.data, type(request.data)) # 封装后的数据
  40. return Response({'code':1000})

使用postman再次发送,效果同上!

关于DRF解析器的源码解析,请参考文章

http://www.cnblogs.com/derek1184405959/p/8724455.html

注意:一般在前后端分离的架构中,前端约定俗成发送json数据,后端接收并解析数据!

解析器到这里就结束了,下面继续讲购物车

判断课程id是否合法

修改views目录下的shoppingcart.py,修改post方法

  1. import redis
  2. from rest_framework.views import APIView
  3. from rest_framework.viewsets import ViewSetMixin
  4. from rest_framework.response import Response
  5. from api import models
  6. CONN = redis.Redis(host='192.168.142.129',port=6379)
  7. class ShoppingCartView(ViewSetMixin,APIView):
  8. # parser_classes = [JSONParser,FormParser] # 指定解析器
  9. def list(self, request, *args, **kwargs):
  10. """
  11. 查看购物车信息
  12. :param request:
  13. :param args:
  14. :param kwargs:
  15. :return:
  16. """
  17. return Response('ok')
  18. def create(self,request,*args,**kwargs):
  19. """
  20. 加入购物车
  21. :param request:
  22. :param args:
  23. :param kwargs:
  24. :return:
  25. """
  26. """
  27. 1. 接受用户选中的课程ID和价格策略ID
  28. 2. 判断合法性
  29. - 课程是否存在?
  30. - 价格策略是否合法?
  31. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  32. 注意:用户ID=1
  33. """
  34. # 1. 接受用户选中的课程ID和价格策略ID
  35. course_id = request.data.get('courseid')
  36. policy_id = request.data.get('policyid')
  37. if course_id.isdigit(): # 判断是否为数字
  38. policy_id = int(policy_id)
  39. else:
  40. return Response({'code': 10001, 'error': '课程非法'})
  41. # 2. 判断合法性
  42. # - 课程是否存在?
  43. # - 价格策略是否合法?
  44. # 2.1 课程是否存在?
  45. course = models.Course.objects.filter(id=course_id).first()
  46. if not course:
  47. return Response({'code': 10001, 'error': '课程不存在'})
  48. return Response({'code':1000})

使用postman发送json数据

Day101 redis操作,购物车,DRF解析器 - 图3

查看返回信息

Day101 redis操作,购物车,DRF解析器 - 图4

数据放入redis

为什么要将购物车数据,放到redis中呢?

因为购物车的操作比较频繁,它是一个临时数据。用户付款后,数据就删除了。

如果使用数据库,速度太慢,影响用户体验!

购物车数据结构

  1. shopping_car_用户id_课程id:{
  2. id:课程ID
  3. name:课程名称
  4. img:课程图片
  5. defaut:默认选中的价格策略
  6. # 所有价格策略
  7. price_list:[
  8. {'策略id':'价格'},
  9. {'策略id':'价格'},
  10. ...
  11. ]
  12. },

为什么要这么设计呢?

其中我们可以使用3层字典嵌套,来展示用户—>课程id—>价格策略

但是redis不支持字典嵌套,所以这样设计,是为了减少字典嵌套。注意:所有价格策略,存的是json数据**!**

判断价格策略id是否合法

修改views目录下的shoppingcart.py,修改post方法

  1. import redis
  2. from rest_framework.views import APIView
  3. from rest_framework.viewsets import ViewSetMixin
  4. from rest_framework.response import Response
  5. from api import models
  6. CONN = redis.Redis(host='192.168.142.129',port=6379)
  7. class ShoppingCartView(ViewSetMixin,APIView):
  8. # parser_classes = [JSONParser,FormParser] # 指定解析器
  9. def list(self, request, *args, **kwargs):
  10. """
  11. 查看购物车信息
  12. :param request:
  13. :param args:
  14. :param kwargs:
  15. :return:
  16. """
  17. return Response('ok')
  18. def create(self,request,*args,**kwargs):
  19. """
  20. 加入购物车
  21. :param request:
  22. :param args:
  23. :param kwargs:
  24. :return:
  25. """
  26. """
  27. 1. 接受用户选中的课程ID和价格策略ID
  28. 2. 判断合法性
  29. - 课程是否存在?
  30. - 价格策略是否合法?
  31. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  32. 注意:用户ID=1
  33. """
  34. # 1. 接受用户选中的课程ID和价格策略ID
  35. course_id = request.data.get('courseid')
  36. policy_id = request.data.get('policyid')
  37. if course_id.isdigit(): # 判断是否为数字
  38. policy_id = int(policy_id)
  39. else:
  40. return Response({'code': 10001, 'error': '课程非法'})
  41. # 2. 判断合法性
  42. # - 课程是否存在?
  43. # - 价格策略是否合法?
  44. # 2.1 课程是否存在?
  45. course = models.Course.objects.filter(id=course_id).first()
  46. if not course:
  47. return Response({'code': 10001, 'error': '课程不存在'})
  48. # 2.2 价格策略是否合法?
  49. # 查看当前课程所有价格策略
  50. price_policy_queryset = course.price_policy.all()
  51. price_policy_dict = {} # 空字典
  52. for item in price_policy_queryset:
  53. temp = {
  54. 'id': item.id, # 价格策略id
  55. 'price': item.price, # 价格
  56. 'valid_period': item.valid_period, # 有效期
  57. 'valid_period_display': item.get_valid_period_display() # 有效期中文
  58. }
  59. price_policy_dict[item.id] = temp # 循环加入到空字典中
  60. # policy_id类型必须为数字,否则即使存在,这里也会提示价格策略不存在
  61. if policy_id not in price_policy_dict: # 判断价格策略是否存在
  62. return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'})
  63. return Response({'code':1000})

使用postman再次发送,效果同上!

发送一个不存在的价格策略id

Day101 redis操作,购物车,DRF解析器 - 图5

查看返回值

Day101 redis操作,购物车,DRF解析器 - 图6

商品信息存入redis

修改views目录下的shoppingcart.py

  1. import redis
  2. import json
  3. from rest_framework.views import APIView
  4. from rest_framework.viewsets import ViewSetMixin
  5. from rest_framework.response import Response
  6. from api import models
  7. CONN = redis.Redis(host='192.168.142.129',port=6379)
  8. USER_ID = 1 # 固定用户id
  9. class ShoppingCartView(ViewSetMixin,APIView):
  10. # parser_classes = [JSONParser,FormParser] # 指定解析器
  11. def list(self, request, *args, **kwargs):
  12. """
  13. 查看购物车信息
  14. :param request:
  15. :param args:
  16. :param kwargs:
  17. :return:
  18. """
  19. return Response('ok')
  20. def create(self,request,*args,**kwargs):
  21. """
  22. 加入购物车
  23. :param request:
  24. :param args:
  25. :param kwargs:
  26. :return:
  27. """
  28. """
  29. 1. 接受用户选中的课程ID和价格策略ID
  30. 2. 判断合法性
  31. - 课程是否存在?
  32. - 价格策略是否合法?
  33. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  34. 注意:用户ID=1
  35. """
  36. # 1. 接受用户选中的课程ID和价格策略ID
  37. course_id = request.data.get('courseid')
  38. policy_id = request.data.get('policyid')
  39. if course_id.isdigit(): # 判断是否为数字
  40. policy_id = int(policy_id)
  41. else:
  42. return Response({'code': 10001, 'error': '课程非法'})
  43. # 2. 判断合法性
  44. # - 课程是否存在?
  45. # - 价格策略是否合法?
  46. # 2.1 课程是否存在?
  47. course = models.Course.objects.filter(id=course_id).first()
  48. if not course:
  49. return Response({'code': 10001, 'error': '课程不存在'})
  50. # 2.2 价格策略是否合法?
  51. # 查看当前课程所有价格策略
  52. price_policy_queryset = course.price_policy.all()
  53. price_policy_dict = {} # 空字典
  54. for item in price_policy_queryset:
  55. temp = {
  56. 'id': item.id, # 价格策略id
  57. 'price': item.price, # 价格
  58. 'valid_period': item.valid_period, # 有效期
  59. 'valid_period_display': item.get_valid_period_display() # 有效期中文
  60. }
  61. price_policy_dict[item.id] = temp # 循环加入到空字典中
  62. # policy_id类型必须为数字,否则即使存在,这里也会提示价格策略不存在
  63. if policy_id not in price_policy_dict: # 判断价格策略是否存在
  64. return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'})
  65. # 3. 把商品和价格策略信息放入购物车
  66. pattern = 'shopping_car_%s_%s' % (USER_ID, '*',) # key的格式
  67. keys = CONN.keys(pattern) # 搜索key,比如:shopping_car_1_* *表示模糊匹配
  68. if keys and len(keys) >= 1000: # 如果key的长度大于1000。意思就是买了1000门课程
  69. return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'})
  70. key = "shopping_car_%s_%s" %(USER_ID,course_id,) # 单个课程
  71. CONN.hset(key, 'id', course_id) # 存入课程id
  72. CONN.hset(key, 'name', course.name)
  73. CONN.hset(key, 'img', course.course_img)
  74. CONN.hset(key, 'default_price_id', policy_id)
  75. # 由于价格策略有很多个,需要json一下
  76. CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))
  77. CONN.expire(key, 60*60*24) # key的有效期为24小时
  78. return Response({'code': 10000, 'data': '购买成功'})

发送一个正确的值

Day101 redis操作,购物车,DRF解析器 - 图7

查看返回结果

Day101 redis操作,购物车,DRF解析器 - 图8

使用xhsell登录redis,查看所有的key

使用命令:keys *

  1. 127.0.0.1:6379> keys *
  2. 1) "shopping_car_1_1"

查看key的所有信息

使用命令: hgetall shopping_car_1_1

  1. 127.0.0.1:6379> hgetall shopping_car_1_1
  2. 1) "default_price_id"
  3. 2) "2"
  4. 3) "name"
  5. 4) "Python\xe5\xbc\x80\xe5\x8f\x91\xe5\x85\xa5\xe9\x97\xa87\xe5\xa4\xa9\xe7\x89\xb9\xe8\xae\xad\xe8\x90\xa5"
  6. 5) "id"
  7. 6) "1"
  8. 7) "price_policy_dict"
  9. 8) "{\"1\": {\"valid_period_display\": \"1\\u5468\", \"valid_period\": 7, \"price\": 10.0, \"id\": 1}, \"2\": {\"valid_period_display\": \"1\\u4e2a\\u6708\", \"valid_period\": 30, \"price\": 50.0, \"id\": 2}}"
  10. 9) "img"
  11. 10) "Python\xe5\xbc\x80\xe5\x8f\x91\xe5\x85\xa5\xe9\x97\xa8"

再购买一个课程

Day101 redis操作,购物车,DRF解析器 - 图9

注意:价格策略id是唯一的,看价格策略表

Day101 redis操作,购物车,DRF解析器 - 图10

这里展示的价格策略id,就是价格策略表的主键id

object_id 表示course表的主键id,表示具体哪门课程。

content_type_id为8,表示course表。为什么8就是course表呢?

查看django_content_type表,因为主键id为8的。就是course表!

Day101 redis操作,购物车,DRF解析器 - 图11

查看所有key

  1. 127.0.0.1:6379> keys *
  2. 1) "shopping_car_1_2"
  3. 2) "shopping_car_1_1"

查看购物车记录

修改views目录下的shoppingcart.py

  1. import redis
  2. import json
  3. from rest_framework.views import APIView
  4. from rest_framework.viewsets import ViewSetMixin
  5. from rest_framework.response import Response
  6. from api import models
  7. from api.utils.serialization_general import SerializedData
  8. CONN = redis.Redis(host='192.168.142.129',port=6379)
  9. USER_ID = 1 # 固定用户id
  10. KEY_prefix = 'shopping_car' # 购物车key的前缀
  11. class ShoppingCartView(ViewSetMixin,APIView):
  12. # parser_classes = [JSONParser,FormParser] # 指定解析器
  13. def list(self, request, *args, **kwargs):
  14. """
  15. 查看购物车信息
  16. :param request:
  17. :param args:
  18. :param kwargs:
  19. :return:
  20. """
  21. ret = {'code': 10000, 'data': None, 'error': None} # 状态字典
  22. try:
  23. shopping_car_course_list = [] # 空列表
  24. pattern = "%s_%s_*" % (KEY_prefix,USER_ID,) # 默认匹配用户的购物车
  25. user_key_list = CONN.keys(pattern)
  26. for key in user_key_list:
  27. temp = {
  28. 'id': CONN.hget(key, 'id').decode('utf-8'), # 解码
  29. 'name': CONN.hget(key, 'name').decode('utf-8'),
  30. 'img': CONN.hget(key, 'img').decode('utf-8'),
  31. 'default_price_id': CONN.hget(key, 'default_price_id').decode('utf-8'),
  32. # 先解码,再反序列化
  33. 'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
  34. }
  35. shopping_car_course_list.append(temp)
  36. ret['data'] = shopping_car_course_list # 状态字典增加key
  37. except Exception as e:
  38. ret['code'] = 10005
  39. ret['error'] = '获取购物车数据失败'
  40. return Response(ret)
  41. def create(self,request,*args,**kwargs):
  42. """
  43. 加入购物车
  44. :param request:
  45. :param args:
  46. :param kwargs:
  47. :return:
  48. """
  49. """
  50. 1. 接受用户选中的课程ID和价格策略ID
  51. 2. 判断合法性
  52. - 课程是否存在?
  53. - 价格策略是否合法?
  54. 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR
  55. 注意:用户ID=1
  56. """
  57. # 1. 接受用户选中的课程ID和价格策略ID
  58. course_id = request.data.get('courseid')
  59. policy_id = request.data.get('policyid')
  60. if course_id.isdigit(): # 判断是否为数字
  61. policy_id = int(policy_id)
  62. else:
  63. return Response({'code': 10001, 'error': '课程非法'})
  64. # 2. 判断合法性
  65. # - 课程是否存在?
  66. # - 价格策略是否合法?
  67. # 2.1 课程是否存在?
  68. course = models.Course.objects.filter(id=course_id).first()
  69. if not course:
  70. return Response({'code': 10001, 'error': '课程不存在'})
  71. # 2.2 价格策略是否合法?
  72. # 查看当前课程所有价格策略
  73. price_policy_queryset = course.price_policy.all()
  74. price_policy_dict = {} # 空字典
  75. for item in price_policy_queryset:
  76. temp = {
  77. 'id': item.id, # 价格策略id
  78. 'price': item.price, # 价格
  79. 'valid_period': item.valid_period, # 有效期
  80. 'valid_period_display': item.get_valid_period_display() # 有效期中文
  81. }
  82. price_policy_dict[item.id] = temp # 循环加入到空字典中
  83. # policy_id类型必须为数字,否则即使存在,这里也会提示价格策略不存在
  84. if policy_id not in price_policy_dict: # 判断价格策略是否存在
  85. return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'})
  86. # 3. 把商品和价格策略信息放入购物车
  87. pattern = '%s_%s_%s' % (KEY_prefix,USER_ID, '*',) # key的格式
  88. keys = CONN.keys(pattern) # 搜索key,比如:shopping_car_1_* *表示模糊匹配
  89. if keys and len(keys) >= 1000: # 如果key的长度大于1000。意思就是买了1000门课程
  90. return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'})
  91. key = "%s_%s_%s" %(KEY_prefix,USER_ID,course_id,) # 单个课程
  92. CONN.hset(key, 'id', course_id) # 存入课程id
  93. CONN.hset(key, 'name', course.name)
  94. CONN.hset(key, 'img', course.course_img)
  95. CONN.hset(key, 'default_price_id', policy_id)
  96. # 由于价格策略有很多个,需要json一下
  97. CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))
  98. CONN.expire(key, 60*60*24) # key的有效期为24小时
  99. return Response({'code': 10000, 'data': '购买成功'})

使用postman发送get请求,不需要参数

Day101 redis操作,购物车,DRF解析器 - 图12

购物车删除

删除购物车,需要传入一个课程id。通过url传参就可以了

修改api_urls.py,增加delete

from django.conf.urls import url
from api.views import course,degreecourse,auth,shoppingcart

urlpatterns = [
    url(r'auth/$', auth.AuthView.as_view({'post':'login'})),
    url(r'courses/$',course.CoursesView.as_view()),
    url(r'courses/(?P<pk>\d+)/$',course.CourseDetailView.as_view()),

    url(r'shoppingcart/$', shoppingcart.ShoppingCartView.as_view({'get':'list','post':'create','delete':'destroy'})),
]

修改views目录下的shoppingcart.py

import redis
import json
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from api import models
from api.utils.response import BaseResponse

CONN = redis.Redis(host='192.168.142.129',port=6379)
USER_ID = 1 # 固定用户id
KEY_PREFIX = 'shopping_car'  # 购物车key的前缀

class ShoppingCartView(ViewSetMixin,APIView):
    # parser_classes = [JSONParser,FormParser]  # 指定解析器

    def list(self, request, *args, **kwargs):
        """
        查看购物车信息
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = {'code': 10000, 'data': None, 'error': None}  # 状态字典
        try:
            shopping_car_course_list = []  # 空列表

            pattern = "%s_%s_*" % (KEY_PREFIX,USER_ID,)  # 默认匹配用户的购物车

            user_key_list = CONN.keys(pattern)
            for key in user_key_list:
                temp = {
                    'id': CONN.hget(key, 'id').decode('utf-8'),  # 解码
                    'name': CONN.hget(key, 'name').decode('utf-8'),
                    'img': CONN.hget(key, 'img').decode('utf-8'),
                    'default_price_id': CONN.hget(key, 'default_price_id').decode('utf-8'),
                    # 先解码,再反序列化
                    'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
                }
                shopping_car_course_list.append(temp)

            ret['data'] = shopping_car_course_list  # 状态字典增加key

        except Exception as e:
            ret['code'] = 10005
            ret['error'] = '获取购物车数据失败'

        return Response(ret)

    def create(self,request,*args,**kwargs):
        """
        加入购物车
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        """
        1. 接受用户选中的课程ID和价格策略ID
        2. 判断合法性
            - 课程是否存在?
            - 价格策略是否合法?
        3. 把商品和价格策略信息放入购物车 SHOPPING_CAR

        注意:用户ID=1
        """

        # 1. 接受用户选中的课程ID和价格策略ID
        course_id = request.data.get('courseid')
        policy_id = request.data.get('policyid')

        if course_id.isdigit():  # 判断是否为数字
            policy_id = int(policy_id)
        else:
            return Response({'code': 10001, 'error': '课程非法'})

        # 2. 判断合法性
        #   - 课程是否存在?
        #   - 价格策略是否合法?

        # 2.1 课程是否存在?
        course = models.Course.objects.filter(id=course_id).first()
        if not course:
            return Response({'code': 10001, 'error': '课程不存在'})

        # 2.2 价格策略是否合法?
        # 查看当前课程所有价格策略
        price_policy_queryset = course.price_policy.all()
        price_policy_dict = {}  # 空字典
        for item in price_policy_queryset:
            temp = {
                'id': item.id,  # 价格策略id
                'price': item.price,  # 价格
                'valid_period': item.valid_period,  # 有效期
                'valid_period_display': item.get_valid_period_display()  # 有效期中文
            }
            price_policy_dict[item.id] = temp  # 循环加入到空字典中

        # policy_id类型必须为数字,否则即使存在,这里也会提示价格策略不存在
        if policy_id not in price_policy_dict:  # 判断价格策略是否存在
            return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'})

        # 3. 把商品和价格策略信息放入购物车
        pattern = '%s_%s_%s' % (KEY_PREFIX,USER_ID, '*',)  # key的格式
        keys = CONN.keys(pattern)  # 搜索key,比如:shopping_car_1_*   *表示模糊匹配
        if keys and len(keys) >= 1000:  # 如果key的长度大于1000。意思就是买了1000门课程
            return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'})

        key = "%s_%s_%s" %(KEY_PREFIX,USER_ID,course_id,)  # 单个课程

        CONN.hset(key, 'id', course_id)  # 存入课程id
        CONN.hset(key, 'name', course.name)
        CONN.hset(key, 'img', course.course_img)
        CONN.hset(key, 'default_price_id', policy_id)
        # 由于价格策略有很多个,需要json一下
        CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))

        CONN.expire(key, 60*60*24)  # key的有效期为24小时

        return Response({'code': 10000, 'data': '购买成功'})

    def destroy(self, request, *args, **kwargs):
        """
        删除购物车中的某个课程
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        response = BaseResponse()
        try:
            courseid = request.GET.get('courseid')  # 获取课程id
            key = "%s_%s_%s" % (KEY_PREFIX,USER_ID,courseid)  # 获取redis中的课程id

            CONN.delete(key)  # 删除单个key
            response.data = '删除成功'

        except Exception as e:
            response.code = 10006
            response.error = '删除失败'

        return Response(response.dict)

使用postman,发送带参数的get请求

提示删除成功

Day101 redis操作,购物车,DRF解析器 - 图13

查看购物车,发现只有一个课程

Day101 redis操作,购物车,DRF解析器 - 图14

修改价格策略

这里只要选择了一个价格策略,会发送一个ajax请求。后端会修改redis中的数据

修改用户购物车的默认价格策略id

Day101 redis操作,购物车,DRF解析器 - 图15

修改api_urls.py,增加put

from django.conf.urls import url
from api.views import course,degreecourse,auth,shoppingcart

urlpatterns = [
    url(r'auth/$', auth.AuthView.as_view({'post':'login'})),
    url(r'courses/$',course.CoursesView.as_view()),
    url(r'courses/(?P<pk>\d+)/$',course.CourseDetailView.as_view()),

    url(r'shoppingcart/$', shoppingcart.ShoppingCartView.as_view({'get':'list','post':'create','delete':'destroy','put':'update'})),
]

修改views目录下的shoppingcart.py

import redis
import json
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from api import models
from api.utils.response import BaseResponse

CONN = redis.Redis(host='192.168.142.129',port=6379)
USER_ID = 1 # 固定用户id
KEY_PREFIX = 'shopping_car'  # 购物车key的前缀

class ShoppingCartView(ViewSetMixin,APIView):
    # parser_classes = [JSONParser,FormParser]  # 指定解析器

    def list(self, request, *args, **kwargs):
        """
        查看购物车信息
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = {'code': 10000, 'data': None, 'error': None}  # 状态字典
        try:
            shopping_car_course_list = []  # 空列表

            pattern = "%s_%s_*" % (KEY_PREFIX,USER_ID,)  # 默认匹配用户的购物车

            user_key_list = CONN.keys(pattern)
            for key in user_key_list:
                temp = {
                    'id': CONN.hget(key, 'id').decode('utf-8'),  # 解码
                    'name': CONN.hget(key, 'name').decode('utf-8'),
                    'img': CONN.hget(key, 'img').decode('utf-8'),
                    'default_price_id': CONN.hget(key, 'default_price_id').decode('utf-8'),
                    # 先解码,再反序列化
                    'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
                }
                shopping_car_course_list.append(temp)

            ret['data'] = shopping_car_course_list  # 状态字典增加key

        except Exception as e:
            ret['code'] = 10005
            ret['error'] = '获取购物车数据失败'

        return Response(ret)

    def create(self,request,*args,**kwargs):
        """
        加入购物车
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        """
        1. 接受用户选中的课程ID和价格策略ID
        2. 判断合法性
            - 课程是否存在?
            - 价格策略是否合法?
        3. 把商品和价格策略信息放入购物车 SHOPPING_CAR

        注意:用户ID=1
        """

        # 1. 接受用户选中的课程ID和价格策略ID
        course_id = request.data.get('courseid')
        policy_id = request.data.get('policyid')

        if course_id.isdigit():  # 判断是否为数字
            policy_id = int(policy_id)
        else:
            return Response({'code': 10001, 'error': '课程非法'})

        # 2. 判断合法性
        #   - 课程是否存在?
        #   - 价格策略是否合法?

        # 2.1 课程是否存在?
        course = models.Course.objects.filter(id=course_id).first()
        if not course:
            return Response({'code': 10001, 'error': '课程不存在'})

        # 2.2 价格策略是否合法?
        # 查看当前课程所有价格策略
        price_policy_queryset = course.price_policy.all()
        price_policy_dict = {}  # 空字典
        for item in price_policy_queryset:
            temp = {
                'id': item.id,  # 价格策略id
                'price': item.price,  # 价格
                'valid_period': item.valid_period,  # 有效期
                'valid_period_display': item.get_valid_period_display()  # 有效期中文
            }
            price_policy_dict[item.id] = temp  # 循环加入到空字典中

        # policy_id类型必须为数字,否则即使存在,这里也会提示价格策略不存在
        if policy_id not in price_policy_dict:  # 判断价格策略是否存在
            return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'})

        # 3. 把商品和价格策略信息放入购物车
        pattern = '%s_%s_%s' % (KEY_PREFIX,USER_ID, '*',)  # key的格式
        keys = CONN.keys(pattern)  # 搜索key,比如:shopping_car_1_*   *表示模糊匹配
        if keys and len(keys) >= 1000:  # 如果key的长度大于1000。意思就是买了1000门课程
            return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'})

        key = "%s_%s_%s" %(KEY_PREFIX,USER_ID,course_id,)  # 单个课程

        CONN.hset(key, 'id', course_id)  # 存入课程id
        CONN.hset(key, 'name', course.name)
        CONN.hset(key, 'img', course.course_img)
        CONN.hset(key, 'default_price_id', policy_id)
        # 由于价格策略有很多个,需要json一下
        CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))

        CONN.expire(key, 60*60*24)  # key的有效期为24小时

        return Response({'code': 10000, 'data': '购买成功'})

    def destroy(self, request, *args, **kwargs):
        """
        删除购物车中的某个课程
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        response = BaseResponse()
        try:
            courseid = request.GET.get('courseid')  # 获取课程id
            key = "%s_%s_%s" % (KEY_PREFIX,USER_ID,courseid)  # 获取redis中的课程id

            CONN.delete(key)  # 删除单个key
            response.data = '删除成功'

        except Exception as e:
            response.code = 10006
            response.error = '删除失败'

        return Response(response.dict)

    def update(self, request, *args, **kwargs):
        """
        修改用户选中的价格策略
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        """
        1. 获取课程ID、要修改的价格策略ID
        2. 校验合法性(去redis中)
        """
        response = BaseResponse()
        try:
            course_id = request.data.get('courseid')
            policy_id = request.data.get('policyid')

            key = '%s_%s_%s' %(KEY_PREFIX,USER_ID,course_id,)  # 获取用户购物车中的单个课程

            if not CONN.exists(key):  # 判断key是否存在
                response.code = 10007
                response.error = '课程不存在'
                return Response(response.dict)

            # 获取所有的价格策略。先解码,再反序列化。最终是一个字典
            price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
            # 由于反序列化之后,字段的key-value都强制转换为字符串了
            # 所以上面获取到的价格策略id必须转换为字符串,才能使用下面的not in 判断
            policy_id = str(policy_id)
            if policy_id not in price_policy_dict:  # 判断价格策略id是否存在
                response.code = 10008
                response.error = '价格策略不存在'
                return Response(response.dict)

            CONN.hset(key, 'default_price_id', policy_id)  # 修改默认的价格策略id
            CONN.expire(key, 60*60*24)  # 重新设置有效期为24小时,之前的有效期会被覆盖!
            response.data = '修改成功'

        except Exception as e:
            response.code = 10009
            response.error = '修改失败'

        return Response(response.dict)

使用postman发送put请求,注意带上参数

Day101 redis操作,购物车,DRF解析器 - 图16

查看返回值

Day101 redis操作,购物车,DRF解析器 - 图17

总结:

a. 为什么要把购物车信息放到redis中?
    - 查询频繁
        - 课程是否存在?
        - 价格策略是否合法?
    - 中间状态 
        - 购买成功之后,需要删除。
        - 购物车信息删除

b. 购物车有没有数量限制?
    使用 keys 查看个数做判断,限制为1000。
    如果不限制,会导致redis内存占满,导致内存溢出!

c. 购物车的结构 
    shopping_car_用户id_课程id:{
        id:课程ID
        name:课程名称
        img:课程图片
        defaut:默认选中的价格策略
        # 所有价格策略
        price_list:[
            {'策略id':'价格'},
            {'策略id':'价格'},
            ...
        ]
    },
d. 对于字典的key,序列化会将数字转换成字符串
比如:
info = {1:'xiao',2:'zhang'}
new = json.dumps(info)  # 结果为 '{"1":"alex", "2":"于超"}'
data = json.loads(new)  # 结果为 {"1":"alex", "2":"于超"}

作业:

1. 虚拟机安装上redis,redis服务启动
2. 购物车,完成以下功能:
    - 添加到购物车
    - 查看购物车信息
    - 删除课程
    - 修改课程