(个人笔记)Django学习笔记整理

‘@’ 为遗漏点或者难点
‘#’ 为重点标记
'若有打眼,欢迎自取,错误之处,还请指教

    DAY 11.24
    
   @ url统一资源定位符限定有2到4kb 因为浏览器地址栏里只能输入这么多东西
    
    1,MVT
    2,观看中文文档
    3,学习重点 视图 模型
    4,i课件回顾。
    5,新建项目,在pycharm里新建项目 选择django框架,选择虚拟环境,
    workon meiduo  which python
    然后复制路径拷贝到虚拟环境选项。
    6,项目文件介绍。
    7,那啥是啥意思来着?就是需要定义路由规则?
    8,框架自带服务器和部署服务器的区别。  自带服务器是什么?
    9,setting是配置文件的原因是在manage文件中指定了配置命令,所以可以修改。
    10,创建应用,startapp  
              注册/安装 应用。
    11,contrib 提供的 
    12,试图函数必须有接受参数,表示请求对象。
    13, 配置路由规则。from . import views
    当前包引入视图模块
    14,url语法 ,第一个参数是争着表达式,第二个参数是试图模块里的视图函数名。
    @复习正则表达式
    abspath  绝对路径
    host=【】放的是域名
    15,前后端分离不用csrf,交给网站实现
    16,root_urlconf 指定根级路由文件 
    17, 模板文件配置信息 绑定了路径信息
    18, 指定wsgi
    19, 指定数据库
    20,auth 用户认证模块,做密码验证的 大概和注册部分的方式有关。
    21,指定静态文件按请求目录,改的时候要绑定文件路目录。
    @老师用的的啥输入法?答 五笔
    22,路由规则:
    url字符串:协议://域名:端口/路径/?查询字符串
    匹配过程  拿到url 找到路径部分 删除最左边的斜杠,与根级url进行按顺序匹配。
    匹配失败就返回404
    匹配成功 就删除成功的部分,留下剩余的部分如index/再与下一级的列表按顺序匹配,失败返回404 成功则返回结果。
    @与flask匹配区别,逐级匹配,不成功则不返回详细映射。
    
    位置参数和关键子参数的区别,在与返回的参数是否是按位置排列,或者按关键字参数排列?
    
    
    @匹配级别 怎么分的  根级包含的子级 有咩有会混淆的可能啊?感觉好乱啊
    23,django中设置路由规则不能以/开头,推荐/结尾。
     
    
    24,从request中拿数据,四种途径 就是四个信息方面。
          24.1 路径中:
    路径中对应的部分加括号,并且要再函数中加参数提取回来。
    @怎么知道别人给了几个参数?也就是怎么知道设置几个变量来接收/?
    Da:肯定知道参数名,因为客户端的请求是按前端定义好的参数格式发送的,不是手动输入的,是鼠标点的。
    @正则表达式可以直接按位置再url里命名。
    
    url(r'^a/([a-z]+)/(\d{4})/$', views.a),
    def a(request, city,year):
        print('city=%s' % city )
        print(year)
        return HttpResponse('ok')
    
    url(r'^b/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.b),
    def b(request, year, city):
        print(city)
        print(year)
        return HttpResponse("ok")
         24.2 查询字符串
    路径后面以?开始 &连接的字典形式的参数就是查询字符串。
    获取用GET.get 
    
    url('^get1/$',views.get1)
    
    def get1(request):
        dict1 = request.GET
        print(dict1)
        print(dict1.get('a'))
        return HttpResponse(dict1.get('a')
    
    
    怎么获取指定参数?肯定知道参数名,因为客户端的请求是按前端定义好的参数格式发送的,不是手动输入的,是鼠标点的。
        24.3 请求报文体 json等
    表单类型的数据:request.POST
    重要:只要请求体的数据是表单类型,无论是哪种请求方式(POST、PUT、PATCH、DELETE),都是使用request.POST来获取请求体的表单数据
    非表单数据:request.body 返回json格式数据。
    @用postman模拟post a=10
    @json格式的数据构造必须是“”双引号才能解析。是postman的问题。  获取用request.body 并转换数据。
    步骤为,1 接受byte类型数据 byte
                   2 转换为字符串dir =  byte .decode
                   3 (导入json包)转换为字典x'husn'husn'wei json.loads(dir)
        24.4 请求报文头 mehtods
    request.属性 常用的有
    request.method
    request.path
    request.user  当前用户  登陆用户或者游客。
    @上面获取参数的意思是 这些东西为以后写接口做准备。
    25,返回jsonresponse格式数据 return JsonResponse({字典格式的数据})括号里写数据。
    如果不是json格式数据,设置字典的value值为  safe=False
    @其实括号里有两个参数  一个json数据 一个safe  默认true 所以不是字典就用false。
    @无论如何返回的是json格式的数据。
    
    def json1(request):
        # request.body===>接收请求体中的非表单数据,常用格式为json,类型为bytes
        str1 = request.body.decode()  # 转字典串
        # 将字符串转字典
        # json.dumps()===>字典转字符串
        # json.loads()===>字符串转字典
        dict1 = json.loads(str1)
        print(dict1.get('a'))
        return HttpResponse('OK')
    
    ------状态保持-----
    26,cookie 键值对方式存在浏览器中,
    写法 response.set_cookie('','',max_age='')
    def cookie_set(request):
        response = HttpResponse('OK')
        response.set_cookie('a', 10, max_age=7 * 24 * 60 * 60)
        return response
    
    读取 request.cookies.get('key')
    
    def cookie_get(request):
        a = request.COOKIES.get('a')
        print(a)
        return HttpResponse('OK')
    
    27,状态保持之session
    保存在redis里  复制过来 必须有一项default
    保存在缓存中  再保存到redis中。
    如果存成一样的东西 记得手动分开。
    @将caches设置成动态模板!!
    写法:request.session['hello']='django'
    
    def session_set(request):
        # request.session['a'] = 10
        request.session['b'] = 20
        return HttpResponse('OK')
    
    读取:request.session.get(‘hello’)
    
    def session_get(request):
        print(request.session.get('a'))
        return HttpResponse('ok')
    
    删除 1  :del request.session(''key)
    删除2 : request.session.clear()
    删除3: request.session.flush()
    def session_del(request):
        # del:将指定的键、值进行删除
        # del request.session['b']
    
        # clear:将所有的键、值删除
        # request.session.clear()
    
        # flush:删除整条数据
        # request.session.flush()
    
    
    
    28,session加密存储的信息是将key和值整体加密存进redis中的,不是分开存的。
    @session默认过期时间是2周。
    @cookie默认过期时间是关闭浏览器。
    @session 依赖与cookie。
    
        return HttpResponse('OK')
    29,类视图  实现请求方式不一样的时候返回视图不一样。  并实现代码继承和复用。
    
    class ShowView(View):
        def get(self, request):
            return HttpResponse('get')
    
        def post(self, request):
            return HttpResponse('post')
    
    
    def show(request):
        if request.method == 'GET':
            return HttpResponse('get')
        elif request.method == 'POST':
            return HttpResponse('post')
    
    」
    —————————
    
    DAY 11.25
    昨日总结:
    
    今日记录:
    1,session 当作字典用就好了
    2,类视图的as_view方法 是源码带的
    他内部定义一个函数并返回了一个函数。所以路由规则里写的也是满足规则的一个函数
    url('^.....view......as_view)
    3,dispatch 派遣调度。
    -----
    100 continue 继续发送
    101 switch 转换协议
    
    200 ok
    
    301 moved permanently 重定向了
    
    400 badrequest 不能解析
    401 unauthorized  要求登陆
    403 forbidden 禁止访问
    404 notfound 定向失败
    405 methods notallowd 请求方法不允许
    406 not acceptable 客户端无法接收
    410 请求页面丢失
    
    
    500 internal server error 服务器未知错误
    501 not lmplenmented 服务器不支持请求
    502 bad gateway  服务器网关无效
    504 gateway timeout 网关超时
    505 httpversion not supported 服务器不支持请求的协议版本
    -------------
    4,getattr 获得属性
    类视图 就是根据据请求方法找方法然后执行。
    
    5,类视图使用装饰器。再内部最先执行的函数钱加装饰器。  这里用的是dispatch()(也有get  post等已经执行的方法)
                 也可以导入utils 导入类装饰包
    @method_decorator(wrapper1,name='dispatch')
    6,扩展功能类 以***Mixin结尾  表示功能类  用于特定的功能的封装。 
    使用方法,写个类继承他一下。
    
    7,中间件 
     7.1 是什么  相当于请求钩子,为了给所有的试图函数加上一定的功能。功能类似于一个装饰器。比装饰器更装饰器!!中间件可以包含装饰器。
    7.2 选择标准,看装饰的对象的多少。如果大部分需要装饰,可以用中间件然后排除掉不用装的视图,就是做if判断。
    7.3 定于语法与装饰器相同
    7.4 注册到setting中,middleware
    7.5 排除写个if判断
    7.6 中间件的执行顺序:按照注册顺序,以试图函数为分界,先进后出。
    
    8,模板 templates决定这么文件路径的关键是已经在setting里注册了
    return里面的参数:1 request, 2 xx.html 3,字典参数
    并在html里接受字典的key进行渲染。
    @注意与jingja2里不一样的语法指出是 运算符左右各有一个空格。
    @过滤器参数冒号后面加  
    
    @复习 输出直接{{ 变量 }}
                运算{% 函数 %}
    @自定义过滤器 与jinja2类似 看下文档。
    ----------------------
    9,数据库  的orm  对象关系映射
    安装包pymysql,
    9.1 在项目同名文件夹下的init文件中,
    import pymysql
    pymysql . install_as_MySQLdb()
    @解释 就是pymysql升级了  但是django中没有升级内部接口名称,执行后就相当于把pymysql 当作MySQLdb用。
    
    9.2  需要在注册在数据库配置中
    参数:DATABASES = {复制一下}
    
    10,定义模型类。
    在应用的model下定义模型类
    语法:
    class ***(models.Model):
    各种语法:复制
    默认主键
    默认表名
    明确定义的属性 和隐含的属性
    (BookInfo, related_name = '自定义属性名’)
    
    @自关联一对多 多对多等关系
    @onotoone等
    
    11,数据库迁移
    两行命令 1 makemigrations 生成迁移文件。
                2   migrate 执行迁移文件
    12, 进入shell惊醒数据库的增删改查
    @执行记录查询语句的配置  相当于echo语句打印吧
    12.1,语法一:
         objects.create()
    创建完成后需要用浏览器执行?
      语法2:
    book = BookInfo()
    book.btitle='abc'
    ..
    ..
    book.save()
    save执行了update然后insert
    @比语法一复杂。 一般用于用户加密后存入数据库。
    然后post执行???
    @浏览器执行相当于什么???
    12.2单条件 查询
    get 单一对象
    all() 查询全部 一般返回给模板遍历。要结合条件查询
       filter 满足条件
      exclude 满足的排除
      det过滤单一结果。
    @语法 属性名称__比较运算符 = 1等于值 id__exeat=1  | id = 1
    2包含  contents
    3 以什么开头 startwith
    4 以什么结尾endwith
    @@@前面加上I是表示不区分大小写。
    5,isnull  = false 或者ture
    6,范围 in  1,3  表示或  不是在        1,3 之内
    7, 不等于3  exclude(id = 3)
    8,对日期做运算:__year = 1980  发布日期为1980年的书。
    9, 日期比较大小  gt=1980.1.1
    大于1980.1.1的日期。」
    —————————
    
    
    DAY 11.27
    
    昨日总结
    1,复习,创建 模型;
    	1:模型类.object.create(属性1=值1)
    	2:对象类型=模型类()
    2,查询:
    	模型类.object.方法(属性_运算符=值)
    	方法:
    	运算符.
    3, F 对象 对象的属性:
    	F('属性名称') 可以让属性和属性比较,而不是直接属性值来比较,就是变量与变量比较.  
    	bread__gt=F('bcomment') * 2 
    4,Q对象 ,为了连接多个条件语句
    	fillter(Q(bread__gt=20)&Q(pk__lt=3))
    	@pk 表示primary key  上那么主键都可以用.
    5, 逻辑与 & 可以用,代替.
    6, 逻辑或 | ===>管道连接
    7, 逻辑非 ~ ====>对Q对象取非.
    8, aggregate 聚合函数 后面还有che类型
    	aggregate(sum())
    	返回的shug是字典类型数据
    9,count 
    10, 排序  :order_by 以什么排序 默认升序.
    	降序前面加 - 减号
    11, 没有查询分页方法 使用的是一个类方法.
    12, 关联查询:??????
    	使用对象访问:对象,和属性
    	查询语句,对象_属性_运算符 = 值
    	
    13, 修改
    	put方法
    14, 删除方法 deleat逻辑删除
    15, 查询集:调用all() filter() ,exclude()
    order_by() 返回查询集
    	俩个特性 :1,惰性执行
    			2,缓存,减少与mysql交互,迅速.会根据代码实现.
    
    16,admin 后台管理.
     16.1 创建管理员:
     16.2 注册模型类:
     16.3 修改中文  类属性增 verbors_name
    17, 后台管理 之
    	17.1 增删改差
    	17.2 增加后台属性 定义一个类然后作为注册的属性传入修改效果.
    	17.3 strftime  日期转字符串.
    		 strptime  字符串转换日期格式.
    	17.4 list_display 列表页面显示什么方法
    	17.5 list_filter 列表页面以什么过滤
    	17.6 search_fields 列表页面搜索选项. 需要指定搜索范围.
    18, 编辑页的属性修改
    	fields=[属性名称]  设置编辑页面的属性
    	用于编辑页面的高级修改  就跟设置某些页面的高级选项一样.  不写的属性不会显示.
    	fieldsets=(
    		('组名',{'fields':[属性名])
    		('组名',{'fields':[属性名)	
    	)
    
    19, 内嵌类  定义类 加参数 然后在书籍列表嵌入英雄.   可以给书籍列表增加修改项目 
    	列表新式内嵌 和 form表单新式内嵌.
    20, 后台页面的标题  
       3个  标签标题
    		首页标题
    		主页标题
    
    21, 上传图片.文件保存在磁盘  名字或者地址保存到数据库.
    	1,完成配置,创建目录,配置地址.
    	2,模型类中定义属性,ImageField
    @需要数据库迁移,需要文件copy   ???如果不copy的话会怎么样?
    @需要在medie下新建保存库.
    	3,如果需要管理编辑在admin下的管理模型下面还要注册新增类.
    	@图片保存路径构造: media/
        4, 访问需要注册文件目录.
    	
    
    DAY 11.28
    
    昨日总结
    
    数据库操作
    	增加:
    		对象=模型类.objects.create(属性1=值1,...)
    		创建对象,属性赋值,对象.save()
    	查询
    		模型类.objects.方法(属性__运算符=值)
    		方法:all()===>不能写参数
    			filter(),get,exclude()====>写查询条件
    			order_by(),aggregate()
    		类型:F(属性)
    			Q(条件语句)==》实现逻辑与&、或|、非~
    			说明:逻辑与可以通过逗号连接
    	修改
    		模型类.objects.filter(条件).update(属性1=值1,...)
    		查询对象,修改属性,对象.save()
    	删除
    		模型类.objects.filter(条件).delete()
    		查询对象,对象.delete()
    
    @还没写到admin  写到了  但是迁移不了数据库
    
    @后台管理admin
    	1.创建管理员
    	2.在应用的admin.py中注册模型类
    		class ***Admin(admin.ModelAdmin):
    			重写属性,实现页面效果
    		admin.site.register(模型类,***Admin)
    	3.在后台中可以完成数据的增加、修改、删除、查询
    
    @repr 和str 方法显示字符串
    
    @shell(输入python)输出对象时会执行这个对象的repr方法
    
    @在终端输出 比如pycharm里输出的那个终端使用str的方法.
    
    @ shell  计算机解释器界面
    今日笔记
    1,DRF Django REST framwork
    2, web应用模式 :
    	前后段不分离 接受数据 处理数据 返回数据
    	前后段分离  处理数据返回数据  返回json
    区别在与试图负责的工作,  不分离的试图负责调用模板 生成html返回.  分离的试图负责构造json返回数据. 
    3, RESTful 设计方法.
        一般写接口就是尽量在一个类中定义不同方法.遵守规范. 这个规范即使RESTful规范.
        含义:规定编写接口的规则:    
        1,应用域名尽量体现api
        2, 版本信息体现.
    
    @案例
    图书
    	操作:crud
    	定义视图:get,post,put,delete
    	路由规则:一条
    英雄
    	操作:crud
    	定义视图:get,post
    	路由规则:三条
    -----------与代码有关部分-------------
        3, 路径:名词 复数 原则.
        4,HTTP动词都是请求方法  所以和路径区别开了.
    	常用四个  模型类的方法
               GET () 查寻 无数据也是get方法
               POST ()  新建
               PUT  () 修改
               DELETE ()  删除
       	不常用四个 PATCH ()
       			HEAD  ()
       			OPTIONS () 
         5,
         6,状态码 
           200 ----ok          
           201 ---- created    创建成功
           204 ---- no content 无返回值 删除成功
           
           301 ----moved permanently
           
           400 ----bad request
           403 ----forbidden
           404 ----not found
           406 ----methods not allowd
          7,处理异常
          8,返回结果
          增删改查要返回结果.
          
          9, 超媒体 API的文档信息 让前端或者用户阅读.
          10, 数据格式 json格式 
    -----------------------------------
    
    3,代码实现
    请求方式   处理         路径            响应      状态码
    get ------查询--------books/----------json-------200
    post------查询--------books/----------json-------201
    get2----根据主键---books/(?P<pk>\d+)/--json-------200
    put-------修改----books/(?P<pk>\d+)---json--------201
    delete----删除-----books/(?P<pk>\d+)----无---------204    
        
    @实现: 创建工程---创建应用---复制模型---配置数据库---注册应用---注册路由---创建视图
    
    @格式和转换失败 报错 非json不识别  json认识的是字典 需要将返回的对象转字典
    
    @实现就是用一个新列表遍历得到的数据并append进去,需要提取key,并注意转换日期格式.
    
    @序列化  就是将没有序列的结果转换的有序列 序列恶意理解为就是变成字典格式. 
    
    @json是一种数据格式 与语言无关 他的来源是基本额数据格式,所以查询到的数据必须通过格式化再转成json.  基本数据格式(int float tuple set
    str 字典)
    如
     {
          "bpub_date": "2018-11-28",
          "btitle": "西游记456"
        }
    
    4, post方法
    创建(post传输)
    接收:   json_dict = json.loads(request.body.decode())
    
    book = BookInfo.objects.create(**json_dict)
    
    return JsonResponse({
            '':''
            '':''
    })
    @注意不要转换日期格式
    @stats = 201 加在return内 可以转换状态码
    
    5, get 根据主键查询一个对象
    
    
    6,修改 put方法
        1,接收
        2,验证
        3,查询   需要判断 
           保存 save()
        return jasonresponse
    7,删除 delete
           注意返回信息
           
           
    8 , 封装重复代码  
    
    @试一下在哦啊上的返回书包含的英雄数据是否可以转换成列表 
    
    9,DRF 框架 实现操作数据库转换json格式
     序列化 :对象转换成json
     反序列化 :字典json转换成对象  里面包含了验证的过程.
     @完成了封装和后台交互.
    环境搭建
    1.pip install 
    2.在settings.py中INSTALLED_APPS=['rest_framework']
     
    10, 序列化器类 serializer serializer
        10.1 定义 创建serializers文件 并定义 序列化器类型.
        导包---定义类
        serializer类:
        class ***Serializer(类的父类)
        属性=类型
        @属性与模型类的属性同名 就可以.
     包含数据类型  字符串 日期 数字 
        10.2 重点选项说明
            read only  只读 只用于输出.  = bollen
            write only 只写 反序列化的输入 不用与输出
            required---->必须,如果请求报文中没有这个属性,则抛异常
    11,  代码实现序列化器
         序列化多个对象(blist,many=True)!!!
        return (safe=False)
    格式
    serializer=序列化器类型(模型类对象)
    serializer.data====>返回字典
    12 关系属性的序列化
    参数不同  关系型字段 并设置rea_only = True
    返回关联参数如果需要输出为字符串而不是关系字段,需要字符串需要调用字符串方法,并指定属性是read_only
    3种输出方案:
    \	1.输出主键
    	2.输出字符串
    	3.输出自定义序列化器
    ------数据需求是功能决定的----
     
    
    
     DAY 11.30
    复习
    1,RESTful规范
    2,序列化与反序列化 类似与数字化反数字化等,我们的角度是将字典与对象来回转换.
    3,调用序列化语法:
    	1,serializer = ***Serializer(data=zidian)
    	2,serizlizer.is_valid()
    	验证是否序列化
    	3,save() 
    ----->代码实现:
    
    4,验证方式
     1,验证类型 和 选项 
    required=False?  必须=false 表示如果有这个参数,但是没写不报错.
     2,自定义验证方法,验证单个属性
    ----->代码块
    def validate_btitle(self,value):
    	#验证是否包含django字段
    	if 'django' not in validate: 
    		raise serializers.ValidationError('书名必须包含django')
    	return value	
    @验证就是为了找错,不用try expect
     3,针对多个属性比较验证.
    def validate(self,attrs):
    	bread = attrs.get('bread')
    	bcomment = attrs.get('bcomment')
    	if bread < bcomment:
    		raise serializers.ValidationError('阅读量必须大于等于评论量')
    
    	return attrs
    @返回值不提示具体字段,因为这不是某一个字段的错误.
     
    4, 创建对象
    	调用save() 方法实际执行create()方法.
        所以需要重写create才嫩实现保存.
    ----->代码块
      引入模型,然后调用类方法:
    def create(self, validated_data):
     book = BookInfo.objects.create(**validated)
     return book
    
    5,修改方法 
    ----->代码块
        def update(self, instance, validated_data):
            # 修改
            # instance===>待修改的对象
            # validated_data===>接收的,经过验证后的数据
            # 对于必传属性,直接获取并赋值
            instance.btitle = validated_data.get('btitle')
            instance.bpub_date = validated_data.get('bpub_date')
            # 对于可传属性,判断再赋值
            if validated_data.get('bread'):
                instance.bread = validated_data.get('bread')
            if validated_data.get('bcomment'):
                instance.bcomment = validated_data.get('bcomment')
            instance.save()
            return instance
    调用代码:需要传入对象名 和新的值.
    
    @创建方法 和修改方法 意义不同,如果没有创建 就不能修改.但是如果已经创建,修改可以多次执行,并且会实现相当于覆盖的效果的创建方法.???
    
    6,模型类序列化器:
    ModelSerializer方法 比 serializer进行更高级的封装.
    封装的部分:1,定义属性的代码,
    		  2,
    未封装的部分:验证方法 等
    ----->代码块.
    class HeroSerializer(serializers.ModelSerializer):
        # 隐藏属性,需要明确定义
        hbook_id = serializers.IntegerField()
        # 关系属性
        hbook = serializers.StringRelatedField(read_only=True)
    
    @注意单独输出属性用fields=[]列表指明.
    @比如隐含属性  可以明确定义 下面不管用类方法还是单独方法输出都不会报错.
    @添加格外属性的方法:
    ------>代码块.
        # 自定义验证方法
        def validate_hname(self, value):
            # 要求:姓郭
            if not value.startswith('郭'):
                raise serializers.ValidationError('姓郭')
            return value
    @为啥没觉得没怎么封装啊??
    
    7,选择序列化器:
     标准:是否操作模型类
    
    8,DRF的视图部分:(另一部分是序列化器)
    @新对象方法
    @新的类:重点APIVIEW
    ----->代码块
    #导入包 :from rest_framework.views import APIView
    #创建类()继承自apiview
    class HerosView(APIView):
        def get(self, request):
            # 接收查询字符串中的条件
            pk = request.query_params.get('pk')
            ordering = request.query_params.get('ordering')
            # 查询多个
            hlist = HeroInfo.objects.filter(pk__gt=pk).order_by(ordering)
            serializer = serializers.HeroSerializer(hlist, many=True)
    		#返回response()
    		return Response(serializer.data)
    @查询一个对象:
    ----->代码块
    class HeroView(APIView):
        def get(self, request, pk):
            # 根据主键查询一个
            try:
                hero = HeroInfo.objects.get(pk=pk, is_delete=False)
            except:
                return Response({'msg': '无效编号'}, status=status.HTTP_404_NOT_FOUND)
            # 序列化
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data)
    
    @post修改
    ----->代码块
        def post(self, request):
            # 创建
            # 1.接收
            json_dict = request.data
            # 2.验证、保存
            serializer = serializers.HeroSerializer(data=json_dict)
            # 验证失败抛异常
            serializer.is_valid(raise_exception=True)
            hero = serializer.save()  # ===>create()
            # 3.响应
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
    
    @注意没有定义创建个修改方法,因为继承了
    
    @delete()
    ----->代码块
        def delete(self, request, pk):
            # 删除指定主键的对象
            # 1.查询
            try:
                hero = HeroInfo.objects.get(pk=pk)
            except:
                return Response({'msg': '无效编号'}, status=status.HTTP_404_NOT_FOUND)
            # 2.删除
            # hero.delete()
            hero.is_delete = True
            hero.save()
            # 3.响应
            return Response(status=status.HTTP_204_NO_CONTENT)
    注意要执行save()
    @patch() 方法修改部分属性,可以不传必传参数. 实现部分属性修改,需要在序列化器里传入partial=true  指明部分属性修改.并用patch方法访问.
        def patch(self, request, pk):
            # 修改部分属性
            # 1.查询:
            try:
                hero = HeroInfo.objects.get(pk=pk)
            except:
                return Response({'msg': 'not found'}, status=status.HTTP_404_NOT_FOUND)
            json_dict = request.data
            # 局部修改关键字 partial=True
            # 还注意传参数需哟字典,并且注意逗号.
            serializer = serializers.HeroSerializer(hero, data=json_dict, partial=True)
            serializer.is_valid(raise_exception=True)
            hero = serializer.save()
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data)
    
    总结:
    1,序列化过程:
    2,序列化器实现数据的创建和更新:
    (创建,save())
    区别在于创建序列化器部分的传入参数;
    3,用drf的request获取请求数据
    data 获取请求体的数据
    query_params  获取查询字符串
    其他好像一样.
    3,使用response构造响应对象.
    4,使用序列化器尽心反序列化操作:验证,与保存
    4.1,实现验证方式:
    三个部分验证:1,2,3,
    4.2 实现保存 create()
                update()
    5,认识APIView视图 
    
    (小结:必背部分
    list-------查询多个 get
    retrieve---查询一个get
    create-----创建post
    update-----修改put
    destroy----删除 delete
    )
    
    
    
    DAY 12.1
    1,GenericAPIView  框架
    两个属性重点:
    queryset = 查询集
    serializer_class = 序列化器类
    五个操作类型的函数:
     ListModelMixin
     CreateModleMixin
     RetrieveModelMixin
     
    
    
    ----->代码块
    # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
    class BookDetailView(GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            book = self.get_object()
            serializer = self.get_serializer(book)
            return Response(serializer.data)
    
    
    @先写视图的大概模型,同时写serializer类  ,需要什么序列化器赶紧加上.
    
    @继承的关系 MRO顺序
    ----->代码块
    class BookDetailView(APIView, GenericAPIView):
    
    @习惯上先写misin的类 再写generic的类
    2,查询全部,的get和post方法ListModelMixin
    ---->代码块
    from rest_framework.mixins import ListModelMixin
    
    class BookListView(ListModelMixin, GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request):
            return self.list(request)
    
    @只能适合统一路由的,路由不同不能继承.
    3,:查询一个 修改 更新 视图:
    ----->代码块:
    class BookDetailView(RetrieveModelMixin, GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            return self.retrieve(request)
    
    @注意patch方法:
    ---->代码块
    @注意删除方法:物理删除和逻辑删除:
    ---->代码块
    class DestroyModelMixin(object):
        """
        Destroy a model instance.
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
    
        def perform_destroy(self, instance):
            instance.delete()
    
    4,进一步简化:统一路由下的方法抽取:
    ----->代码块
    @方法省略.因为请求方法是固定不能修改的得 ,由浏览器决定.
    ----->类名分类:
    5,不存在的类原因:UpdateDsetroyAPIView
    需自定义,其实不存在这个情况.不会先更新一个再删除一个的情况????
    
    6.单数查询一个大类:ListAPIView:
    ----->代码块;
    提供 get 方法
    继承自:GenericAPIView、ListModelMixin
    @因为类都封装了,所以以后一般工作在验证.
    
    ??????
    实现
    
    
    7,进一步封装试图类: 视图集ViewSet:
    	.list() 提供一组数据
    retrieve() 提供单个数据
    create() 创建数据
    update() 保存数据
    destory() 删除数据
    
     @解决的1问题:路由规则封装
    ----->as_view({方法的字典})
    urlpatterns = [
        url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
        url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
    ]
    
     @解决问题2:重写dispatch
    @#@ 提供的参数 action 因为请求方法一致,但是具体的行为不一样,就指定action参数来分配具体方法.
    
    @类似于男厕女厕两大类,但是具体inque进去小便还是大便不一样,并且小便大便还有可以选择吨位还是站位,!!!
    
    @最最最终类:ModelViewSet!!!
    包括了增删改查所有类的方法.
    
    @查询类 ReadOnlyViewSet
    
    @添加自定义方法 用装饰器@action(methods=['get], detail=False)
    
    参数详解:
    def get_serializer_class(self):
        if self.action == 'create':
            return OrderCommitSerializer
        else:
            return OrderDataSerializer
    常用视图集父类
    1) ViewSet
    继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
    在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
    2)GenericViewSet
    继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
    3)ModelViewSet
    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
    4)ReadOnlyModelViewSet
    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。
    视图集中定义附加action动作
    在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。
    添加自定义动作需要使用rest_framework.decorators.action装饰器。
    以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
    action装饰器可以接收两个参数:
    methods: 该action支持的请求方式,列表传递
    detail: 表示是action中要处理的是否是视图资源的对象(即是否通过url路径获取主键)
    True 表示使用通过URL获取的主键对应的数据对象
    False 表示不使用URL获取主键
    举例:
    from rest_framework import mixins
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.decorators import action
    
    class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        # detail为False 表示不需要处理具体的BookInfo对象
        @action(methods=['get'], detail=False)
        def latest(self, request):
            """
            返回最新的图书信息
            """
            book = BookInfo.objects.latest('id')
            serializer = self.get_serializer(book)
            return Response(serializer.data)
    
        # detail为True,表示要处理具体与pk主键对应的BookInfo对象
        @action(methods=['put'], detail=True)
        def read(self, request, pk):
            """
            修改图书的阅读量数据
            """
            book = self.get_object()
            book.bread = request.data.get('read')
            book.save()
            serializer = self.get_serializer(book)
            return Response(serializer.data
    
    
    具体路由区别
    ---->
    urlpatterns = [
        url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
        url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
        url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
        url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
    ]
    
    8,路由集:
    ----->代码块 
    from rest_framework import routers
    
    router = routers.SimpleRouter()
    router.register(r'books', BookInfoViewSet, base_name='book')
    2)添加路由数据
    可以有两种方式:
    urlpatterns = [
        ...
    ]
    urlpatterns += router.urls
    
    或
    urlpatterns = [
        ...
        url(r'^', include(router.urls))
    ]
    
    @可以设置为快捷键模板
    @DefaultRouter() 和 SimpleRoute()
    区别?
    DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。
    9,配置环境:
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',   # 基本认证
            'rest_framework.authentication.SessionAuthentication',  # session认证
        )
    }
    
    在视图中单独设置
    
    也可以在每个视图中通过设置authentication_classess属性来设置
    from rest_framework.authentication import SessionAuthentication, BasicAuthentication
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        authentication_classes = (SessionAuthentication, BasicAuthentication)
        ...
    
    @认证作用域的要点:
    
    @权限要求中的不同区别,也有作用域
    
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        permission_classes = (IsAuthenticated,)
        ...
    
    @限流  限制频率/100值得是次数.
    可以限制频率单位,可以给小视频分类点击次数.
    ??这个次数记录到哪里了?
    只针对独立用户,不累加.
    
    from rest_framework.throttling import UserRateThrottle
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        throttle_classes = (UserRateThrottle,)
        ...
    
    
    @过滤:
    不支持模糊查询.
    pip install django-filter
    
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
        filter_fields = ('btitle', 'bread')
    
    # 127.0.0.1:8000/books/?btitle=西游记
    
    @排序 相当于之前排序的代码部分
    
    @分页 每页几条数据 单位是条 不是行 会附加页码信息和连接.
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 100  # 每页数目
    }
    关闭分页
    pagination_class = None
    
    from rest_framework.pagination import PageNumberPagination
    
    class StandardPageNumberPagination(PageNumberPagination):
        page_size_query_param = 'page_size'
        max_page_size = 10
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all().order_by('id')
        serializer_class = BookInfoSerializer
        pagination_class = StandardPageNumberPagination
    
    # 127.0.0.1/books/?page=1&page_size=2
    
    ####@###$#这些功能大部分都嫩单独写入试图类,以达到针对性限制.
    
    @异常处理 之后会重写该功能 以达到log记录异常.
    
    	APIException 所有异常的父类
    ParseError 解析错误
    AuthenticationFailed 认证失败
    NotAuthenticated 尚未认证
    PermissionDenied 权限决绝
    NotFound 未找到
    MethodNotAllowed 请求方式不支持
    NotAcceptable 要获取的数据格式不支持
    Throttled 超过限流次数
    ValidationError 校验失败
    
    
    @版本信息
    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
    }
    
    
    @生成项目文档!!!!
    pip install coreapi
    
    设置接口文档访问路径
    在总路由中添加接口文档路径。
    文档路由对应的视图配置为rest_framework.documentation.include_docs_urls,
    参数title为接口文档网站的标题。
    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        ...
        url(r'^docs/', include_docs_urls(title='My API title'))
    ]
    
    
    导包----配置路由规则-----访问  
    (相当于网页形式的项目说明) 
    包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如
    class BookListCreateView(generics.ListCreateAPIView):
        """
        get:
        返回所有图书信息.
    
        post:
        新建图书.
        """
    就是注释 并让网页看得到的注释
    
    
    文档网页
    浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档。
    
    需要中文的化要在试图里用文档注释.
    序列化器里加参数help_text 和admin用的文档说明不冲突.
    
    10 美多商称项目
    
    @看得到的不是新的,想得到才是
    
    模块分析 5大模块:
    1用户
    2 商品
    3 购物中心
    4 订单
    5 支付
    
    @自身价值
    1沟通能力
    2代码基本能力
    3解决问题能力  (如果解决不了,记住也是方法) 
    
    DAY 12.3
    
    总结:及时复习
    2,复习drf附加功能
    GenericAPIView+5个ModelMixin:
    	完成crud的操作
    	queryset=查询集
    	serializer_class=序列化器类型
    ModelViewSet:
    	将多个类中的方法写在同一个视图为
    	属性action:表示当前请求需要调用的方法名称
    	装饰器action:可以在5个方法的基础上,增加新的方法
    		methods=[请求方式]
    		detail=True或False:是否接收主键
    DefaultRouter:
    	在使用视图集时,通过这个类的对象生成路由规则
    	router=DefaultRouter()
    	router.register(前缀,views.视图集类型,base_name=前缀)
    	urlpatterns+=router.urls
    drf配置(复制):
    	身份认证的方式
    	权限判断
    	流量控制
    	过滤
    	排序
    	分页
    	读取版本
    	异常处理
    	生成文档
    
    1,创建工程目录:
    2,修改导包路径 
    	import sys 里面是解释器查询包的路径列表  sys.path.insert(0, os.psth.join(BASE_DIR,'apps')
    @ 不设置也可以需要每次拼接路径
    3,新建数据库:
    	@新建用户---create user meiduo_sy18 identified by 'meiduo';
                 分配权限---grant all on meiduo18.* to 'meiduo_sy18'@'%';(分配给用户 操作数据库在任意ip.
    4,连接数据库,初始化
      配置缓存并连接redis 配置信息 注意安装django-redis
    5,配置本地化信息
    6,注释csrf中间件,模板文件目录.
    7,注册框架 rest_fraework
    8,注册日志模块.logging
    @注意修改路径  向上转移层
    9,异常处理:新建utils工具包,新建exceptions异常文件.  复制
       #@#@:配置drf的异常处理器
    
    10::::查看老师代码从gitee上看.
    
    11, 设置域名
    	在host文件下修改域名映射
    	11.1 访问前端路由映射
    	11.2 访问后端文件路由映射.
    	2.www.meiduo.site=========>访问前端
    	3.api.meiduo.site==========>访问后端
    	 @那以后的路由呢?
    12,跨域问题解决
       12.1 浏览器访问前端文件域名 返回的html解释 css 渲染 js 执行
    	12.2 axios.post请求,js的执行会访问后端视图,就发生了跨域 需要发起options试探.
    	@解决方式导入django-cors-headers包,注册应用----注册corsheaders中间件-----配置访问白名单(也就是从哪个域名下的请求), 以达到每个视图后面自动添加optipns 200状态.(此状态就是cookies)
    	@此问题的根源是前后端分离 或者说是前后端域名不同
    	@如果不解决这个问题 会怎样?
    
    13,具体业务逻辑
    	13.1 用户模块 ---models
    	auth用户认证模块 修改
    	@1,导包----定义(继承			AbstractUser)---自定义字段(如手机号)---覆盖原类----迁移
    	@2,定义字段类型判断原则: 怎么读出来的! 按个十百千读	出来的是数字,按字符读出来的是	字符串.
    	@3,配置 设置可以覆盖模型类设置
    	@4,迁移 
    
    
    -----------------
    14,注册功能--注册页面 
     @图新验证码是为了防止重复向手机发送验证码.
    	14.1 发短信 过程
    	@1,先判断发送标记.
    	@2,生成---存入发送标记---存入redis---发送短信
     新建应用
    @没有包提示 设置文件为source root
    @没有模型类操作 继承自APIVIew 用get方法 
    @连接redis数据库()
    @读取标记 命名 拼接字符串.
    @生成速记短信验证码
    @验证码存入shuck会覆盖,
    @调用第三方平台发短信.
    @异步发短信
    @发送短信不应该相信客户端 需要验证数据库.
    @pipeline 管道 相当于查询集类似 就是缓存命令,最终执行,减少与数据库交互次数. ??????/不太懂
    ---->代码实现
        url(r'^sms_code/(?P<mobile>1[3-9]\d{9})/$',views.SMSCodeView.as_view()),
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django_redis import get_redis_connection
    from utils.ytx_sdk.sendSMS import CCP
    from rest_framework import exceptions
    import random
    from . import contants
    
    
    class SmsCodeView(APIView):
        def get(self, request, mobile):
            '''
            发送短信验证码
            :param request: 请求对象,不需要传递
            :param mobile: 手机号,需要传递
            :return: 是否发送成功
            '''
            # 数据保存在redis中,所以获取redis连接
            redis_cli = get_redis_connection('verify_codes')
    
            # 判断此手机号60秒内是否发过短信,如果已经发送则不发送
            sms_flag = redis_cli.get('sms_flag_' + mobile)
            if sms_flag:
                raise exceptions.ValidationError('发送验证码太频繁')
    
            # 随机生成6位短信验证码
            sms_code = random.randint(100000, 999999)
    
            #使用管道方式交互redis
            redis_pipeline=redis_cli.pipeline()
            # 保存验证码
            redis_pipeline.setex('sms_code_' + mobile, contants.SMS_CODE_EXPIRES, sms_code)
    
            # 保存60秒发送标记
            redis_pipeline.setex('sms_flag_' + mobile, contants.SMS_FLAG_EXPIRES, 1)
    
            #执行
            redis_pipeline.execute()
    
            # 发送短信
            # sms_code_expires = contants.SMS_CODE_EXPIRES / 60
            # CCP.sendTemplateSMS(mobile,sms_code,sms_code_expires,1)
            print(sms_code)
    
            # 响应
            return Response({'message': 'OK'})
    
    15, celery 异步
      15.1作用使用框架时,视图函数实现异步.
      15.2 构成:@1:task:耗时任务,且耗时任务的结果和响应无关的东西  可以放在celery中做异步操作.
      		@2:worker:新今晨,执行任务代码.
    		@3:broker:调用任务时,添加任务到队列,并通知worker执行.通常为redis等nosql数据库,记录信息的.
    		@4:queue: 存储执行的任务
    		@5:任务函数.delay(参数)
    ----->代码实现
    pip install celery
    
    @创建celery文件夹//
    在celery_tasks包下创建config.py文件,用于保存celery的配置信息
    broker_url='redis://127.0.0.1:6379/14'
    
    @创建任务,注册任务
    在celery_tasks包下创建main.py文件,用于作为celery的启动文件
    from celery import Celery
    
    # 为celery使用django配置文件进行设置
    import os
    if not os.getenv('DJANGO_SETTINGS_MODULE'):
        os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_api.settings'
    
    # 创建celery应用
    app = Celery('meiduo')
    
    # 导入celery配置
    app.config_from_object('celery_tasks.config')
    
    # 自动注册celery任务
    app.autodiscover_tasks(['celery_tasks.sms'])
    
    @启动工人???????? 在所有异步任务都注册好后启动,只能手动启动???/:      
    
    在celery_tasks/sms/包下创建tasks.py文件,用于保存发送短信的异步任务
    import logging
    
    from celery_tasks.main import app
    from utils.ytx_sdk.sendSMS import CCP
    
    logger = logging.getLogger("django")
    
    
    @app.task(name='send_sms_code')
    def send_sms_code(mobile, code, expires, template_id):
        """
        发送短信验证码
        :param mobile: 手机号
        :param code: 验证码
        :param expires: 有效期
        :return: None
        """
    
        try:
            # result = CCP.send_template_sms(mobile, [code, expires], template_id)
            result = 0
            print(code)
        except Exception as e:
            logger.error("发送验证码短信[异常][ mobile: %s, message: %s ]" % (mobile, e))
        else:
            if result == 0:
                logger.info("发送验证码短信[正常][ mobile: %s ]" % mobile)
            else:
                logger.warning("发送验证码短信[失败][ mobile: %s ]" % mobile)
    
    @调用任务.任务函数.delay(参数)
    在verifications/views.py中改写SMSCodeView视图,使用celery异步任务发送短信
    from celery_tasks.sms.tasks import send_sms_code
    
    class SMSCodeView(GenericAPIView):
        ...
            # 发送短信验证码
            sms_code_expires = str(constants.SMS_CODE_REDIS_EXPIRES // 60)
            send_sms_code.delay(mobile,sms_code,sms_code_expires,1)
    
            return Response({"message": "OK"})
    启动工人命令
    celery -A celery_tasks.main worker -l info
    
    
                                                      
    
    作业:读文档
    	https://yiyibooks.cn/xx/Django_1.11.6/topics/auth/index.html
    	https://jwt.io/
    	http://getblimp.github.io/django-rest-framework-jwt/
    
    
    DAY 12.4
    昨日总结:
    	1,跨域问题解决方案:每个视图中都响应options. 实现过程就是请求钩子类似在中间件中加入响应.
    	2,celery实现异步,主要是为了提升用户体验.在一个进程中实现不阻塞进程. 异步是实现的后端和前端的交互,更客户端没多大关系.
    	3,配置混乱的解决方法,提纲
    
    今日
    1 判断用户名是否存在:
    	1.1 判断继承自哪个类  判断依据是继承来能不能直接用 返回个数.
    	1.2 只要判断用户名返回个数是大于0的 就说明存在.
    	1.3 返回结果 按文档来.
    	1.4 前端校验 就是判断下是否失去焦点后还会不会有异常提示.
    	@连接数据库失败的异常已经被默认处理,不用判断提示.
    	@filter 方法不会抛出异常,查不到就是0.
    2,判断手机号是否存在   和上面一样.
    ------>代码块
    # url(r'^usernames/(?P<username>\w{5,20})/count/$', views.UsernameCountView.as_view()), 
    class UsernameCountView(APIView):
        """
        用户名数量
        """
        def get(self, request, username):
            """
            获取指定用户名数量
            """
            count = User.objects.filter(username=username).count()
    
            data = {
                'username': username,
                'count': count
            }
    
            return Response(data)
    
    3,注册
    	3.1 继承自g.create
    	3.2 创建不需要查询集 需要序列化,指定序列化器类型
    ---->序列化器代码块
    from rest_framework import serializers
    import re
    from django_redis import get_redis_connection
    from .models import User
    
    
    class CreateUserSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(
            min_length=5,
            max_length=20,
            error_messages={
                'min_length':'用户名为5-20个字符',
                'max_length':'用户名为5-20个字符'
            }
        )
        password = serializers.CharField(
            write_only=True,
            min_length=8,
            max_length=20,
            error_messages={
                'min_length':'密码为8-20个字符',
                'max_length':'密码为8-20个字符'
            }
        )
        mobile = serializers.CharField()
        password2 = serializers.CharField(write_only=True)
        sms_code = serializers.CharField(write_only=True)
        allow = serializers.CharField(write_only=True)
    
        def validate_username(self,value):
            if User.objects.filter(username=value).count()>0:
                raise serializers.ValidationError('用户名存在')
            return value
    
        def validate_mobile(self,value):
            if not re.match('^1[3-9]\d{9}$',value):
                raise serializers.ValidationError('手机号格式不正确')
            return value
    
        def validate_allow(self,value):
            if not value:
                raise serializers.ValidationError('必须同意协议')
            return value
    
        def validate(self,attrs):
            #短信验证码
            redis_cli=get_redis_connection('verify_codes')
            key='sms_code_'+attrs.get('mobile')
            sms_code_redis=redis_cli.get(key)
            if not sms_code_redis:
                raise serializers.ValidationError('验证码已经过期')
            redis_cli.delete(key)
            sms_code_redis=sms_code_redis.decode()
            sms_code_request=attrs.get('sms_code')
            if sms_code_redis!=sms_code_request:
                raise serializers.ValidationError('验证码错误')
    
            #两个密码
            pwd1=attrs.get('password')
            pwd2=attrs.get('password2')
            if pwd1!=pwd2:
                raise serializers.ValidationError('两次输入的密码不一致')
    
            return attrs
    
        def create(self,validated_data):
            user=User()
            user.username=validated_data.get('username')
            user.mobile=validated_data.get('mobile')
            # user.password=validated_data.get('password')
            user.set_password(validated_data.get('password'))
            user.save()
    
            return user
    
    	@考虑接受字段 现有不包含的自定义声明
    	@ allow?需要存储嘛//?是 因为前后端分离,不需要通过前端访问服务器.
    	@经判断直接继承自Serializer
    ----->代码块.
    	3.3 验证方法  
    		3.3.1验证名字是否存在\\\
    		@还是验证count值 大于0 就是存在.
    		3.3.2 验证手机号
    		@根据正则表达式判断格式
    		@根据filter后的count值是否存在
    		3.3.3 判断密码是否确认
    		@validate()
    		3.3.4 判断短信验证码
    		@验证短信验证码需要获取手机号,所以一个方法获取不了,需要用validate(self, attrs)方法.
    		@@@@短信验证码的key里不是有手机号码 可以用key获取嘛?      答,不能,因为只能根据key获取.
    		@#注意 对比值的时候需要类型相同.
    		@@@@注意除了字符串类型 其他的类型都能相互转换直接.
    		@不需要输出的 不需要输入的  加上only参数.
    ---------
    一定不要相信眼睛看到的
    ---------
    4, JWT JSON WEB TOKEN
    	4.1 构成:header.pyload.signature.
    		@头部信息 固定
    		@载荷    用户信息
    		@根据前两部分信息和密钥算出签名
    		>>>简而言之就是 服务器有个密码 结合用户信息算出一个密码, 给浏览器返回携带,来回需要对比签名,对的就是保持,不对的就不算保持.jwt主要功能不是密保,是多客户端登录.
    	4.2 配置
    	4.3 注册成功 状态保持
    ------>代码块
      @timedelta 时间差
    DAY 12.06
    昨日总结:  
    1,JWT  生成token
    2,判断会不会,就是能不能实践.
    3,分支 私有分支--muzhe
    			开发阶段合并分支--dev 包括前端等的合并 测试阶段
    			master---上线分支 运维部署分支
    4,原则是少写代码 继承的视图类 可以手写最早的APIView的代码 然后再封装.
    内容四段:接受数据---检查数据----处理数据----响应数据
    
    5,cerely只针对异步			
    
    ------------------
    
    今日:
    
    1,登录: 用自带的类 添加路由并指定响应结果.
    				#指定载荷数据 ---->代码
    2, 具体视图源码中:obtain_jwt_token 接受post请求,完成登录
       jwt提供视图,接受post请求,获得用户和密码, 调用django中的auth类下面的authenticate方法 完成登录,这个方法读取配置,没有配置找到认证类ModelBackend,这个类完成对比用户和密码 进行验证.
    
    3,实现多帐号登录;
    	3.1自定义认证类 并配置 读取配置类
    	3.2
    ---->代码块
    		#判断是否是手机号
    		#else 是用户名
    		问题@用户名 如果注册的是手机号格式,会引起用户登录错误.  解决方法,不让这么注册.
    --------------
    解决错误的方法,从源头改起.
    --------------
    
    4,qq登录
    	4.1 成为开发者
    	4.2 放置图标
    	-----------以下是后端工作
    	4.3 使用Authorization_Code获取Access_Token
    	///oauth2.0==协议版本 就是让开发者使用第三方接口时候能看懂怎么授权(怎么看懂对方提供的数据库信息)的协议.就是说 你这么写才能访问我们的数据库.
    	4.4 获取openid 就是用户在qq网站的身份信息的唯一标识.
    	4.5 openid与我们的账户信息绑定.
    	@简而言之,开发者获取授权信息1,并用授权信息做成链接,客户点击链接,跳转到qq的认证服务器并填写个人信息2,qq服务器判定成功后用信息1+信息2生成信息3==openid,并携带信息3转回开发者网站,开发者网站得到openid 赋值给对应的注册信息字段,完成注册.  因为qq的信息是qq帐号和qq昵称还有密码,不包括手机号,所以开发者的网站会要求绑定手机号. 其实页可以做成就算绑定了包括手机号等信息,页可以要求客户绑定手机号,以达到验证目的. 所以手机号这个部分会实现了重新存入数据库的效果.
    	@开发者需要编写的部分除了前端效果,还有就是将qq授权信息1编写成url信息(拼接字符串),还有回调我的网址.和返回的信息解读,赋值成我们需要到的注册格式,并赋值.
    	
    	4.6.绑定:
    	根据openid判断是否存在相同内容 就状态保持 登录.-----没相同内容,让前端返回个网站,让他绑定信息.----根据绑定的信息查询用户对象,如果有说明用户登录过,返回信息让他检查或者登录-----如果不存在,创建新用户,并创建已被qq授权的用户字段.
    	
    DAY 12.07
    
    1, baseModel 类是新建时间和更新时间 是优厚很多授权服务都需要用的标志,所以可以建立一个模型在工具里,然后让其他授权模型类继承.
    2,auto_now_add = True 自动添加创建时候的当前时间.
    	auto_now 页是当前时间,但是修改时候会修改.
    3, abstarct = True 抽象 = True 不是具体的,不用迁移.
    @类里面只有两个字段 一个是qq唯一标识,和qq账户与本地账户的依赖关系,  但是这里嫩保存其他qq账户的信息嘛?  是!!
    
    4,urllib ----URL的lib协议  一种格式 资源共享协议
    	urlopen(网址) 发起get强求方式的http请求
    	urlencode(字典) 返回查询字符串
      parse_qs(查询字符串)  返回字典
    5, itsdanderous 
      将字典进行加密解密的
      json需要的类
    	serializer=TimedJSONWebSignatureSerializer(私钥,过期时间)
    提供了 dumos(字典) 返回加密字符串
    lodas(加密字符串)  返回解密后的字典.
    
    6,获取url 
    导入工具包---按照官方的说定义方法---按照方法编写视图函数---添加路由信息---根级路由信息包含
    QQ网站携带授权信息访问前端做成的callback网站,这个网站接受信息然后去访问后端的服务器,后端解析携带的信息,存入数据库等,返回给前端,
    7,解析code 获取token
    	7.1query_params.get()
    	7.2django里发起http请求,获取token
    	@获取的token过程 转化字典等???
    8,获取openid
    	@截取data.[10:-4]  这里注意下.  (以后应该嫩用爱爬虫)
    	@response().read()  读取响应报文体的部分.
    9, 绑定信息
    	9.1 使用openid查询数据
    	9.2 查询不到提示绑定 ,并加密返回给callback,(跳转绑定页面)	
    	9.3 查到了 绑定 
    	继承视图类,指定序列化器,验证access_token信息.这里要解密.
    ---------->代码
    	@逻辑  获得的openid没有标识,在没有绑定之前没有任何可以标识的信息,不能保存在服务器,所以需要返回给客户端(浏览器)然后等待他绑定标识信息,最后一起保存在数据库.  这也很好的解决了有的用户没有绑定意外中断,这部分信息就没用了,村起来页没用.  也可以存进去 然后指定时间内没用的化删除?
    	各种验证:
    --------->代码块
    
    @这段脑袋不转了 有点糊涂.
    
    10,用户中心,
    	10.1 视图类 继承自retrieveAPIVIEW
    	@queryset决定怎么获得数据
    	@serializer决定??
    	@难点 调用重写get_object(self)??
    	10.2 获取当前登录用户
    		return self.request.user
    	@主要要求登录 这部分信息是restframerwork的附带信息里的要求登录权限部分???
    	10.3 指定序列化器 
    	完成    
    
    ====================总结
    根据QQ登录的开发文档使用SDK实现QQ登录的链接的获取
    根据QQ登录的开发文档使用SDK实现access_token的获取
    根据QQ登录的开发文档使用SDK实现openid的获取
    	0.配置:开发者信息
    	1.创建对象OAuthQQ
    	2.调用方法:
    		get_qq_login_url()====>生成授权页面的url
    		get_access_token()====>根据code获取token
    		get_openid()========>根据token获取openid
    知道使用itsdangerous进行数据签名的方法及流程
    	tjwss=TimedJSONWebSignatureSerializer(私钥,过期时间)
    	tjwss.dumps(字典)======>加密字符串
    	tjwss.loads(加密字符串)===>字典
    		说明:如果过期,或信息被修改,则抛异常
    理解获取用户基本信息的业务逻辑
    	1.获取对象
    	2.创建序列化器对象
    	3.输出
    	关键:第1步获取对象时,需要重写get_object()方法
    
    
    
    DAY 12.09
    昨天总结: 
    1,get_object()方法.
    2,及时总结当天的知识点.
    3,
    
    今天笔记:
    1,邮箱绑定
    	1.1 请求方式 put 修改内容系列.,指定继承类,然后要求登录,还要指定查询集,并指定序列化器.
    	1.2 经过分析,不需要查询集,当前登录用户就是当前对象,不用查询.所以 改写get_object方法就心.
    	1.3 定义序列化器.
    	在序列化器里验证邮箱,就是要发送邮箱给feu添加的邮箱,
    	1.4 验证邮箱, 配置邮箱服务器信息,使用django自带的模块send_mail来发送邮箱,
    	方法的参数(主题,消息内容,///)
    	1.5 耗时操作,创建成异步任务,
    	任务设置:1创建包,创建任务文件,定义任务fenian,在主main文件里注册任务.
    	1.6 装饰发送方法
    	1.7 用户取邮箱验证,点击地址直接请求后台,后台服务器接受信息,解密获取用户id,根据修改模型类邮箱的属性.
    2, 三级联动设置收获地址.
    	2.1 新建areas应用,注册应用.
    	2.2 省市区查询,查询省  listapiview  -- get.
    			根据省id查询单个市区  用retrveapivie
    			分析后知道用查询集,就是readonlymodelapiview
    	2.3 查询省级地区,用get_queryset方法只能查询一种.
    	2.4 查询省级下面的地区,虽然只查询省级的地区,必须查询所有,才能显示全部地区,但是由于前面已经限制,所以只显示市级地区.
    	2.5 显示市区一级的信息,必须单独设置查询序列化器.
    	@可以看出指定序列化器时候不能满足情况,就要用get序列化器的方法.
    	@复习retrevr方法的查询结果.他是根据id查询一个.
    3, 服务器优化,缓存!
    	3.1 意义,减少数据库交互,将查询结果缓存起来.@缓存到哪儿?答 缓存到缓存里了,缓存默认设置在redis里.就是在服务器内存里.
    	3.2 实现 \-\-\
    4,收货地址: 增加用户地址字段,
    	4.2 设计收货地址模型表.迁移.
    	4.3 创建视图类继承自createapiview,指定序列化器.但是需要重写????因为有可能指定,有可能指定给我自己,是吧???
    	4.4 创建并且查询,因为只创建进数据库,不会显示创建的地址.需要定义方法实现查询.
    	4.5 查询方法继承自list方法,结合create方法,继承自lictcreateapiview.并且需要获取当前对象.
    	@返回结果需要看前端要求.
    	4.6这部分包含增删改查,所以继承自modelViewset.实现删除.
    
    DAY 12.10
    昨日总结:
    1,缓存,不需要查询mysql暂时,缓存到内存中,也就是redis中? 所以 暂时停止mysql,还可以查询到信息.
    2,序列化器的self.context 字典对象,里面有request对象. 使用方法,self.context['request']得到request对象.
    3,
    
    今日笔记
    1,完成收货地址默认的设置
    2,完成地址的title的修改
    
    -------
    3,商品部分
    	3.1 SPU--一种商品只包含名称 不是实例
    			SKU--一款商品包含规格
    
    		陈列总数:排列组合.
    4,3层分类  8张表
    
    5,商品应用 和 广告应用
    6,富文本编辑器  在模型类里面导入.添加需要编辑的字段,信息介绍等字段,就是手动添加描述商品信息的内部分.
    然后迁移数据库.
    7,FastDFS  分布式文件存储系统 相当于第三方文件存储系统.
    	7.1 tracker 追踪
    	7.2 storage 存储读取  通过nginx读取.
    8, docker, 集装箱 虚拟机吧大概就是,可移植环境系统.
    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
    	8.1 构成:
    		镜像:安装好软件的docker
    		容器:运行镜像,将镜像中的软件运行起来.
    
    9, 命令
      9.1 镜像命令
    	9.2 容器命令
    
    10, 记得创建script文件夹,将脚本文件复制进去.
    
    
    修改用户的邮箱属性:
    	1.接收邮箱数据
    	2.查询当前登录的用户对象
    	3.修改
    	UpdateAPIView
    	ModelSerializer
    	关键:get_object()
    	重写序列化器的update()方法:修改完成后,发邮件
    使用celery发送邮件:
    	django.core.mail.send_mail()
    	{user_id:***}===>TJWSS
    激活邮箱:
    	1.接收加密的数据,进行解密
    	2.根据用户编号查询对象
    	3.修改属性并保存
    省市区查询并缓存:
    	ReadOnlyModelViewSet
    		list
    		retrieve
    	AreaSerializer
    	SubAreaSerializer
    	get_queryset()
    		list====>所有省
    		retrieve===>指定编号的某个地区
    	get_serializer_class()
    		list===>id,name
    		retrieve==>id,name,subs
    	缓存:ResponseCacheMixin
    收货地址:增加、查询、修改、删除
    	ModelViewSet
    	重写序列化器中create()方法:创建收货地址需要指定用户对象,而这个值不从客户端接收,而应该是当前登录的用户,需要手动指定
    		validated_data['user_id']=****
    	在视图中重写list方法,重新构造响应结果
    	在视图中重写destroy方法,实现逻辑删除
    ====================反馈
    *** 当在序列化器对象中使用模型类对象时,如create方法中,这个self中有携带了哪些内容呢??另外不清楚request对象中的context属性的内容以及使用方式,麻烦岐哥讲讲 
    答:self===>序列化器对象
    	self.context===>字典,request===>self.context['request']
    	request没有属性context
    王鹏翼 @修改收货地址时候不接受传来的id,这里不太理解,攻击者修改id以达到修改别人的地址可以达到什么效果?快递都发他家去吗? 
    答:重写查询集方法,返回当前登录用户的收货地址
    @收货人地址信息这里,省市区三级做成三张表也可以吧,这样会不会简单点还是复杂了.自己想不出来求解答. 
    答:可以
    	省空间
    ====================收货地址
    ------修改标题
    @action(methods=['put'],detail=True)
    def title(self,request,pk):
    	pass
    ------设置默认收货地址
    @action(methods=['put'],detail=True)
    def status(self,request,pk):
    	pass
    ====================广告表设计
    首页内容:
    	分类信息
    	广告信息
    广告表:
    	广告类型表-----》广告位
    	广告内容表-----》广告的内容,与广告类型表为1:多的关系
    ====================商品表设计
    陈列,摆设
    SPU-----》一款商品
    	设置不同的规格,并指定选项,按规格与选项进行生产
    SKU-----》指定规格选项的商品
    iphoneX--->SPU
    将规格参数指定:
    	颜色:白,黑
    	存储:64G,128G
    生产:
    	白+64G=====》SKU
    		颜色:白
    		存储:64G
    	黑+64G
    	白+128G
    	黑+128G
    陈列总数:2*2=4
    作业:组内讨论,举例SPU、SKU
    参考:京东
    要求:明确表的作用,表间的关系
    ====================CKEditor
    富文本编辑器:在网页中可以编辑漂亮的页面效果
    类型:
    	RichTextField============>编辑器
    	RichTextUploadingField=====>编辑器,可上传文件
    ====================FastDFS
    分布式文件存储系统===》替代七牛云
    构成:
    	tracker===>追踪
    	storage===>存储,读取
    存储:看图
    读取:	客户端---->nginx(8888)---->storage
    ====================Docker
    集装箱
    在开发阶段,会使用多种软件,在服务器布署时,同样需要这些软件
    优化:直接将需要的软件安装在docker中,并完成配置
    	直接将docker拷贝到服务器,在服务器中运行
    构成:
    	镜像image:安装好软件的docker
    	容器container:运行镜像,将镜像中的软件就可以运行起来
    安装:
    	添加私钥:sudo apt-key add gpg
    	安装:sudo dpkg -i docker-ce_17.03.2~ce-0~ubuntu-xenial_amd64.deb
    	增加管理员权限:sudo usermod -a -G docker $USER
    服务命令:
    	sudo service docker start/stop/restart
    镜像命令:
    	docker image ls=========》查看
    	docker image pull 名称====》下载
    	docker load -i 镜像名称====》加载本地镜像
    	docker rm 名称或id======》删除
    容器命令:
    	docker run 参数 镜像名称====>运行镜像,得到容器,此时镜像中的软件会被运行
    	docker container ls -a======>查看所有容器
    	docker container start/stop/kill 名称或id=====>管理容器的运行
    ====================用docker安装FastDFS
    1.拷贝fastdfs_docker.tar文件到ubuntu桌面上
    2.在ubuntu中进入桌面目录,执行命令
    	docker load -i fastdfs_docker.tar
    	docker image ls -a
    3.创建目录
    	进入/var/下,mkdir fdfs
    	进入fdfs,创建tracker,storage
    4.tracker
    	docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker
    	参数说明:
    		d=============》后台运行
    		t=============》在最后指定命令
    		i==============》交互
    		--network=host====》容器中的软件需要联网,使用当前主机的网络
    		--name tracker=====》指定容器的名称
    		-v /var/fdfs/tracker:/var/fdfs====》指定目录映射关系,格式为-v 本地目录:容器目录
    		delron/fastdfs======》镜像名称
    		tracker===========》上面指定了t,在容器运行后执行此命令
    			当前为tracker,表示容器启动后,运行tracker命令,即启动软件tracker
    5.storage
    	docker run -dti --network=host --name storage -e TRACKER_SERVER=本机ip:22122 -v /var/fdfs/storage:/var/fdfs delron/fastdfs storage
    	参数说明:
    		d=============》后台运行
    		t=============》在最后指定命令
    		i==============》交互
    		--network=host====》容器中的软件需要联网,使用当前主机的网络
    		--name storage====》容器名称
    		-e TRACKER_SERVER=本机ip:22122====》指定环境变量,fastdfs不允许本机ip写为127.0.0.1
    			当前是指定tracker服务器的ip和端口
    		-v /var/fdfs/storage:/var/fdfs ====》指定目录映射关系,格式为-v 本地目录:容器目录
    		delron/fastdfs==========>镜像名称
    		storage==============》前面写了-t,此处表示容器启动后运行的命令
    			里面运行了nginx,监听8888端口
    6.拷贝示例的图片文件data.tar.gz到/var/fdfs/storage目录下,删除data目录,解压
    ====================准备示例数据
    1.在admin.py中注册模型类,可以通过django提供的后台完成数据的crud
    2.拷贝goods_data.sql到scripts目录下,执行,添加示例数据
    ====================总结
    能够说出什么是SPU和SKU
    	荣耀8X===>SPU=======>类
    	红色+6+64===>SKU=====>对象
    能够说出FastDFS的组成部分及上传图片的流程
    	组成:tracker,storage
    	上传流程:
    		1.django接收图片文件
    		2.请求tracker,返回可用的storage
    		3.django将文件上传到storage
    		4.storage生成唯一文件名,保存文件,返回文件名
    		5.django保存文件名
    能够记住Docker拉取、查看、删除镜像的命令
    	docker pull 名称
    	docker image ls
    	docker rm 名称或id
    知道如何使用Docker安装及运行FastDFS
    	1-6步
    作业:https://yiyibooks.cn/xx/Django_1.11.6/howto/custom-file-storage.html
    
    DAY 12.12
    昨日总结:
    1, storage:存储和读取 
    		存储:生成唯一标识,作为文件名存储,返回文件名,
    		读取,提供nginx插件,结合ngnix读取.
    2, docker:
    
    今日笔记:
    1, 修改fastDS 配置, 修改日志存储目录
    2,
    3, 安装包,完成上传文件,返回的文件名,
    4,重写storage方法.
    	#好多方法,open读取,不需要
    	#save方法:1,指定配置文件路径,2,上传文件bybuffer,参数content.read() 上传文件对象读取成二进制数据,存储.返回名称['Remote file_id'] 这个名字相当于name,是字段,detail内容是自动生成的,根据路径读取.
    	#url方法:构造url 拼接url返回.
    5,首页静态化:
     5.1 分类数据:静态化的原因是首页(一般)不根据用户查询输出,而是自己查询输出,所以不需要写接口.查出来直接渲染.
    	5.2 查询三级分类信息??
    	5.3 查询广告类信息
    6,定时任务
    	@使用linux的定时器完成在终端执行任务.
    7,查询多个 显示详细商品列表信息.
    	7.1 根据实际需求获取查询分类字段,定义get_queryset,具体获取方法.self.kwargs.get(关键字)
    
    8,全文检索查询商品信息.
    	8.1 load es镜像
    	8.2 安装haystack,
    	8.3 配置信息
    	8.4 在需要查询的库下面建立应用,
    	8.5 建立模板文件渲染 语法{{object:名称}} 
    	8.6 手动生成初始索引文件 
    	8.7 定义视图和序列化器,返回关键字变量和对象.
    	8.8 定义视图,引入haystackViewset,指定索引类
     @在索引类基础上取搜索
     @实际上是在给所有的商品加上索引,索引数据存在es里,读取用heystack,  相当于简单的索引工具.
     
    
    12.13
    昨日总结:
    1,看代码用gitee
    2,
    
    今日笔记
    1,生成详情页面:
    	1.1 查询商品对象\
    	生成celery任务:
    	
    	1.2 渲染
    	1.3 静态化 写文件
    ---->代码块
    查询详细信息难点:
    	>根据id查询所有
    	>根据对象名称查询对应所有图片
    	>查询分类信息 就是类名
      
    原理,就是先查出所有商品,查出所有的选项,地卡尔选项对应的商品id,然后给对应的选项加js,点击的时候将选项输入一个元祖,当元祖中的所有参数拼接成功后,查处对应商品id.
    2, 调用cerely任务
    	2.1 什么时候执行任务?(增删改查的时候)
    	2.2 在admin中增加管理类,以达到显示那些信息.
    	2.3 admin中增加方法,管理效果
    		save() 方法
    		调用任务 delay()
    		@注意连接es的问题
    		delete() 方法
    		注册进site中:
    	@生成静态页面的delay中意义在于,选择某个选项,不让他马上跳回去页面,指导他选完最后跳到静态页面.  这里说的住奥是针对后台管理页面?????
    	
    3,将python文件做成可执行脚本:
    	3.1 目的是批量生成详情页页面文件. 能解决后台逐个数据的添加慢速的效果,有可能实现成批量注入的效果.
    	3.2 在脚本文件夹里实现
    	3.3 脚本文件和项目无关,需要读取项目配置文件.
    	3.4 django初始化,就为了将自己初始化进项目.不然用不到环境变量
    	3.5 实现自动python可执行文件,
    		3.5.1 加权限,
    		3.5.2 执行解释器 解释器的路径 #! 
    		3.5.3 路径不能写成绝对路径,最终写成 #!/usr/bin/env python
    		@env的效果就是查询后面的东西在哪里.
    		3.5.4  最终执行方法 ./**.py回车就完成了
    
    #!/bin/bash
    sudo shuutdown -h now
    然后修改权限
    
    4, 用户浏览记录存取
    	4.1 在redis存取,数据类型string hash list set zset 数据类型的选择需要按照python中的类型来选取.  设计成存5个一组, 因为需要存5个,搜以不能选string,又需要有序,zset的序列是权重值,list的顺序是点击顺序,先进先出,所以list比较好.
    	@srting 也可以吧  就给每个浏览记录成一个0,全apend到string后面,然后读取列表用切片,[-1]读取.
    	redis练习命lin:
    	#添加到第一个 lpush
    	#删除指定元素 lrem key 0 value
    	#删除最后一个  rpop key
    	#列表长度 llen
    	# 读取全部 lrange key 0 -1
      # ltrim key 0 4 截取指定位置元素 其他的删除
    ---->代码块
    @注意判断用户是否登录
    @获取redis里面的顺序,有可能会按照默认id排序,所以不能全部过滤查询,可以单独以id来get.
    @redis里面可以直接存储连接对象码.
    
       
    12.15
    
    今日笔记:
    1,购物车:
    	两种情况:登录,和未登录
    	1.1 登录情况,存redis.
    	1.2 未登录时存cookie.
    2, cookie版购物车:
    	2.1 cookie只能存字符串 要将字典转化成字符串.这里用pickle转换字典嵌套型数据更快. (pik o)和base64 加密解密.
    	2.2,在utils 新建文件
    		dumps(dict1)
    		json_bt = pickle.读mps(dict1)
    		json_64 = base64.b64encode(json_bt)
    
    		json_str = json_64.decode()
    		return json_str
     		loads(str1)
    		json_64 = str1.encode()
    		json_64 = base64.b64enode(json_64)
    		json_dict = pickle.loads(json_bytes)
    		验证:
    入口函数可以让这个页面单独成为一个函数,
    @print(json.dumps(s, default=lambda obj: obj.__dict__))
    ----->代码块
    新建应用,包含路由,定义视图.等
    @难点,在加入购物车时候会有身份判断的验证过程.
    
    @遍历字典
    for key,value in ict.items():
    
    	2.3 查询cookie
    	2.4 修改cookie购物车,原理是局部刷新
    	2.5 删除购物车数据
    	2.6 全选功能  逻辑,就是修改字典里里面的选择状态.
    
    
    3,redis版本的购物车
    	增删改查
    	hash存商品编号和数量
    	hset key 商品编号 商品数量 
      hdel key 商品编号
      hgetall  key
    ---set
    	smenbers key
    	srem key 商品编号
    	3.1判断登录状态然后决定存在redis里
    	#判断方法,request.user
    	逻辑是否报错.
    	try:
    			user=request.user
    	except:
    			None
    	else:
    			return user
    
      user = validate_user(request)
    	if user is None:
    			(存cookie)
    	else:
    			(存redis)
    			#连接redis
    			#hset(拼接用户信息)
    	3.2 查询redis购物车
    		判断是否登录,是
    		查询,连接redis,然后读取hash值,
    		@byte转字典
    		: {int(key1):int(value) for key1 and value in dict.items()
    	3.3 修改redis购物车商品
    		同上
    		@逻辑, 只修改set里面的选择值的字段.
    	3.4 删除购物车商品
    	3.5 全选全不选的逻辑
    
    
    @今日难点: perform_authentication  request.user方法  继承自APIView的所有视图,都已经obeisant默认进行认证身份信息,当请求时候未登录,就会报错.
    	前端应该进行身份信息认证判断,如果没有登录,就应该不返回认证信息的请求头.
    
    
    
    12.16
    昨日:像那东流水,离我远去不可留.
    1, request.user 进行用户身份验证
    2,商品详情页:陈列等
    3, 在方法的参数位置, * 可以将元祖,列表解包.
    
    今日详情:
    1, 合并购物车 
    	逻辑,定义方法,判断是否登录,没登录,终断. 登录的话,读取cookie,遍历读取,连接redis,存入,以cookie为准,然后删除掉cookie中的数据.返回信息
    2, 合并购物车时的登录判断.
      2.1 原始obtain视图类重写,还是调用原始方法,然后新增调用合并购物车的方法.
    ----------->代码
    class LoginView(ObtainJsonWebToken)
    		def post(self, request, *args, **kwargs):
    		response = super().post(request, *args, **kwargs)
    		#重响应报文获取用户id
    @判断嫩否登录?????不等录能到购物车码?
    		user_id = response.data.get('user_id')
    		resopnse = 合并购物车方法
    		return response
    
    @qq帐号无法绑定,是验证csrf的问题,注销后台信息.
    
    
    3, 购物车结算
    	3.1 收货地址已经完成 ,需要完善address代码.
    	3.2 订单中的已选项目的结算展示.是购物车的部分查询信息.
    	
    @冗余数据	
    	3.3 商品订单模型
    	@订单外键主键自定义.
    	
    4,事物 ,begin  开启事物
    			commit   提交事物
    			rollback  回滚
    启用事物:
    with aotmic()
    save point返回变量开启begin
    savepoint_commit() 执行commit
    savepoint_rollback() 执行rollback
    
    @事物curd特性可以抱枕订单提交的完整性,就是在映射的数据库操作上加上4句代码以完成事物的操作.
    
    \
    业务逻辑,知道如何用户登录合并购物车到redis中
    读取cookie数据
    遍历
    写入redis中
    执行
    登录
    qq登录
    理解数据表和表关系
    oderinfo 订单的基本信息,如主键用户收获地址总金额 支付方式 状态
    ordergoods 订单基本信息编号数量评论
    一堆多
    知道怎么eaudjango中使用atomic中怎么启用事物
    
    
    
    12.18
    喂,该你上场了.
    
    昨日总结:
    三级列表再看看
    
    
    今日笔记:
    1, 并发---购买同一商品
    	并发的的可能性不高.概率为0.3%.
    	1.2, 在判断库存完时同时提交订单,就可能并发. 
    	1.3 解决办法,悲观锁
    	select stock from tb_sku where id=1 for update;
    	select_for_update()
    	1.4 乐观锁
    	sql:update tb_sku set stock=2 where id=1 adn stock=7;
    	orm:filter(id = 1, stoc=7).update(stock = 2)
    	@乐观锁需要修改数据库的事物隔离级别.
    	四个级别的含义  这里用read committed
    	@vailadateErrorde的信息可以在页面中显示码?
    
    #@#@#$@老学生的学习问题:z总会将新知识去映射旧知识.这样有理由必.
    
    2, 对接支付宝
    	2.1 生成私钥openssl 2048 位,
    	2.2 生成公钥  公钥添加到支付宝
    	2.3 支付宝的公钥复制回来
    	2.4 两个文件添加到项目
    	2.5 配置信息 商家信息等等
    	2.6 debug=True 表示设置沙盒 False表示 真实支付
    	2.7 安装alipay的包 有的化卸载了
    3,发起支付信息.
    	3.1 生成支付对象
    	@私钥的传入用路径
    	3.2 生成支付参数
    	@回调地址就是支付成功页面
    	3.3 拼接支付地址返回
    	@沙箱二维码登录不是实际登录 并且需要安装沙箱专用app登录.
    	3.4 支付验证修改数据库信息
    	接受返回参数,转字典,弹出sing,创建pay对象,调用verify方法,验证,
    @此处支付宝内部验证gongyao部分已经被修改需要遍历字典,转换字典中的列表数据成为单一字典数据.然后取验证.
    
    @怎么写代码??
    
    4,增强版Xadmin,crud后台.
    	4.1 安装包
    	4.2 配置 url包含等
    	4.3 goos新建adminx文件,注册admin模型类
    	@注意注释掉同类文件,
    	4.4 新建管理类字段.
    
    @队列不等于cerely
    @请问乐观锁会造成死锁码,怎么解决.
    	
    
    昨日总结:
    1,xadmin里面很多不懂
    2, 醉了管所本质并咩有锁 是sql里的update语句嫁了一个where语句,对应的ard为:模型类.object.filter(...).
    
    简单实现,在文本框中监听change或者keyup等时间,发起ajax请求,查询数据,满足条件的返回.
    监听文版狂,查询关键字数据,返回,后台添加关键字,给索引数据添加,收集用户搜索数据等.
    机器学习:智能分析客户的搜索.
    12.19
    今日笔记:
    1,用户模块
    	1.1 xdmin里的用户模块 默认注册.
    	1.2 需要自己定制,需要先取消默认注册
    	-->unregister(models,User)
    	-->注册管理类,将类名作为参数传入.
    	--->is_staff 是否是内部人员.勾选后该账户帐号可以用于登录管理员后台,但是 还有权限问题,不一定在后台操作什么.
    2, 分配权限:
    @难点:组,可以当成角色,就是职位,也就是具有的权限级别,先将权限分配给组,然后把人分进组里.这些人就有了权限.跟具体的团队组的概念不一样.也就是说权限可以分给组也可以附给人.这样的好处是,如果人不在组里了,就没有权限了.页可以实现权限的制衡.
    @后台数据在哪里存的?如果后台数据被盗取可了不得啊.另外超级管理员会相互制约码?也就是说权重会有区别嘛?
    
    
    3,住从配置 完成读写分离. 以实现业务专业化.
    
    业务优化:1,缓存,
    				2,静态化
    				3,服务器的住从配置.
    
    	3.1, 要点, 相同版本的
    	3.2 用docker模拟一台机器.
    	3.3 在docker里 mysql创建从服务器,并创建路径,然后复制主机配置信息传入.记得修改端口.
    	3.4 run镜像,
    	@为什么没有-itd?
    	3.5 启动从数据库mysql;  在主要命令后面指定端口.
    	3.6 主从同步是基于主服务器输出的logs实现的,并且logs页是基于差异读取,原理在日志里生成更改标记,从服务器先查找更改标记.
    
    	----------
    	3.7 先手动同步主从的数据.
    	-->主输出信息导出数据.
    	-->从 读取数据.
    	-->修改配置信息.
    	-->登录主服务器,创建可同步复制的账户.
    	-->
    4,后端实现读写分离.
    	4.1 添加数据库配置
    	4.2 指定数据库路由.
    --->新建db_router代码>
    pass
    @allow_reation  可以用关联操作.
    这个项目不配置的化,不能用关联操作 ?为什么.这是路由操作的原因还是从服务器的缺陷?不配置会怎么样?
    
    5, 项目部署.
    	5.1 收集静态文件
    	css, js , 图片等
    	> 指定赋值静态文件的目录 STATIC_ROOT = 往上找几层才能取找到文件?
      > 采集命令 python /
    	@这个静态文件更前端没关系,放在哪里无所谓.
    	5.2 配置nginx
    	---> 打开ngins配置文件响应前端文件修改
    	--->server节点{
    listen 80;
    server_name page;
    location /{root 前端目录;
    index index.html
      }
    }
    --> 重启  sudo /usr/sbin/nginx
    
    6,修改配置
    	-->配置文件修改成porduct 
    	-->修改debug=false
    	-->跨域白名单端口改掉默认80.
    	2, 修改wsgi.py  
    	-->读取配置文件改为生成环境
    	3,创建uwisgi.ini 配置信息
    	@socket = 127.0.0.1:8001
    	@下面嗯木木录文件路径 拼接 (manage.py文件路径)
    	4,启动uwsgi
    	-->uwsgi --ini uwsgi.ini
    	@可以将uwsgi额配置文件赋值到很多服务器形成集群.
    	
    7,再配置nginx
    upstream
    {
    server 127.0.0.1:8001;
    }
    
    再价格server监听{
    	listen 8000;
    	location /{
    		include uwsgi_params;
    		uwsgi_pass,meiduo_mall
    }
    }
    
    @@nginx里的模块适用很多类型的语言.
    
    8,配置admin路由.转发/admin到uwsgi服务器.
    -->重启uwsgi 先停掉:再启动.
    -->重启nginx 
    @?????这就完了?
    
    @###shell脚本 自动化部署
    
    10:项目总结
    @功能和心智点复习
    @接口明确两点,序列化器和视图的继承类.
    @判断存在可以用count()方法.
    @注册的序列化器,不使用modelserializer的原因:接受值对应不上,2密码需要加密.
    @状态保持 JWT
    @???重写jwt响应数据???
    @重写认证函数:实现多帐号登录.
    @get_object() 方法 在除了post和get之外的往事视图都会用.
    @readonly值查询.
    @地址增删改查 action(method=[],detali=True)
    @celery包含任务,代理人队列和工人.
    @jwt构成:头,载荷和签名  header,pilow,signature.
    @urllib的上那个方法.
    @isdangerous()
    
    
    @首页静态化:查询生成写文件.
    @定时任务,crontab
    @搜索页: haystack,elsaticserch
    @详情页的调用save_model(),delete_model()
    @py文件做成脚本.
    #!/usr/bin/env
    加权限
    ./运行
    ====
    浏览记录
    将最近浏览的商品加入redis中,!!!!这部分忘了
    ====
    @docker
    @fastdfs:tracker,storage,重写django存储类.
    @购物车
    @订单 确认页面,保存订单,并发处理.
    
    
    其他:
    @项目ganglia
    @git
    @xadmin@读写分离
    @部署 静态文件 前端 uwsgi, nginx转发

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值