亲宝软件园·资讯

展开

drf

strawberry* 人气:2

跨域同源

django做跨域同源 需要把csrf去掉 跨站请求伪造

同源

同源机制:域名、协议、端口号相同的同源

简单请求

不写头部请求 跨域会拦截报错缺少请求信息
(1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
    HEAD
    GET
    POST
(2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。
    #vue.js axios -- $.ajax ajax和axios都是基于js的封装框架
支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'

复杂请求

不在简单范围内的请求头和请求方法

写法

access-contorl-allow-origin 请求头 
写了斜杠http://127.0.0.1/ 只能路径之后才可以访问 
直接写ip地址http://127.0.0.1 是全路径下
复杂请求有option请求 进行预警’
contype是规定以什么格式进行上传
支持跨域,复杂请求
由于复杂请求时,首先会发送“预检”请求'options'请求方法,如果“预检”成功,则发送真实数据。
“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
    res['Access-Control-Allow-Headers'] = 'content-type'#请求头部信息
    # res['Access-Control-Allow-Methods'] = 'PUT'#请求方法
    # res['Access-Control-Allow-Origin'] = '*
    全部网址都可以进行访问
    请求头部信息是需要按照 指定的要求文件格式

GIT版本管理工具

集中式的版本管理工具 是把所有项目上线到项目中 集中管理 蹦了之后项目就崩了

分布式 可以直接把整个版本down下来进行开发 但是你开发的东西基于其他人就没办法进行操作 又要等着上传

drf

问题

前后端分离

1.常用vue.js
2.后端给前端json数据
3.后端要想使用drf组件**request.data
4.需要前端返回json数据类型
5.self封装了request属性
self.request.method

移动端盛行

PC端应用

crm项目,前端后端一起写,运行在浏览器上

第二部分 任务

以前会使用很多url绑定视图函数返回

http://127.0.0.1:8000/info/get/
http://127.0.0.1:8000/info/add/
http://127.0.0.1:8000/info/update/
http://127.0.0.1:8000/infohttps://img.qb5200.com/download-x/delete/

请求方法

http://127.0.0.1:8000/info/
    get,获取数据
    post,添加
    put,更新
    delete,删除

现在要遵循restful规范

restful是什么
resultful是一种前后端约定俗称的一种规则,用于程序之间进行数据交换的约定

详细说明
1。url中一般用名词:http:www。baidu.com/article/面向资源编程,网络上东西都视为资源

    1.5筛选条件在url参数中进行传递例如 
    #http://www.baidu.com/article/?page=1&category

2.是一套规则,用于程序之间进行数据交换的约定。 
3.他规定了一些协议,对我们感受最直接的的是,以前写增删改查需要写4个接口,restful规范的就是1 个接口,根据method的不同做不同的操作,比如:
4.get/posthttps://img.qb5200.com/download-x/delete/put/patchhttps://img.qb5200.com/download-x/delete. 初次之外,resetful规范还规定了:    
    - 数据传输通过json    
扩展:前后端分离、app开发、程序之间(与编程语言无关)
5- URL中一般用名词: 
http://www.luffycity.com/article/ (面向资源编程,网络上东西都视为资源)

6建议加上api标识
    url写法
     http://www.luffycity.com/api/v1....(建议,因为他不会存在跨域的问题)        
     注意:版本还可以放在请求头中            http://www.luffycity.com/api/            accept: ... 
7建议用https代替http 
#为了保证数据的安全
9.要返回给用户状态码(建议)
from rest_formwork import status
status.HTTP_200_OK 返回状态码
或者在字典里面返回自定义code状态码
#例data{
#   code:10000
    status:radom_string
#}
- 200,成功
        - 300,301永久 /302临时
        - 400,403拒绝 /404找不到
        - 500,服务端代码错误
10新增的数据返回值(建议)
要返回多个列表套字典格式
    GET HTTP:..WWW.xxx.com/api/user/
    [
        {"id":1,"name":"xxx","age":19}
        {"id":1,name}
    ]
    单条返回字典
11 操作异常要返回错误信息
    {
        error:"Invalid API key"
    }
12 对于下一个请求要返回一些接口:Hypermedia AP
        {
            'id':2,
            'name':'alex',
            'age':19,
            'depart': "http://www.luffycity.com/api/user/30/"
        }

json数据:

JSON:    {        
    name:'alex',        
    age:18,        
    gender:'男'    
}

以前用webservice,数据传输格式xml

以前用webservice,数据传输格式xml。
XML    
<name>alex</name>    
<age>alex</age>    
<gender>男</gender>

使代码更加专业

不使用drf框架

返回json类型数据

django创建一个项目叫做api '接口'
通过jsonresopnese(data,safe=Flase)
jsonresopnese默认支持字典 非字典需要使用safe

约定俗成的返回数据要返回json类型

只使用一个url cbv格式

 #只使用一个url cbv格式
 get 获取数据
 post添加
 put更新
patch局部更新
delete删除 请求方法

基于django可以实现遵循restful规范的接口开发规范

  • fbv 实现比较麻烦
  • cbv 相比较fbv比较简单 根据method做了不同的区分
  • 使用drf比较方便

初识drf

drf是一个基于django开发的组件,本质是一个django的app。 drf可以办我们快速开发出一个遵循restful规范的程序

写接口的时候用到drf

视图

使用CBV

from django.views import View
之前django是继承view
继承apiview
from rest_framework import Apiview
from rest_framework.views import APIView

知识点

继承apiview
1.请求来了先执行视图的dispatch方法
2.版本处理
3.认证
4.权限
5.节流
6.解析器
'''
-解析器,根据用户请求体格式不同进行数据解析,解析之后放在request.data中
如果content-type:x-www-urlencoded,那么drf会根据 & 符号分割的形式去处理请 求体。            user=wang&age=19                 
    如果content-type:application/json,那么drf会根据 json 形式去处理请求体。         
        {"user":"wang","age":19} 
'''


7.序列化
'''
 - 序列化serlizer类 .data,可以对QuerySet进行序列化,也可以对用户提交的数据进行校验。 
'''
8.筛选器
9.分页
10.渲染器
 - 渲染器,可以帮我们把json数据渲染到页面上进行友好的展示。(内部会根据请求设备不同做不同的 展示)

1.安装地址

pip3 install djangorestframework

2.需要先注册app!!!

如果有新建的app也需要进行注册,要不然不识别

admin不会自动加载

model数据库表结构也不会执行同步

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework'
]

3.写路由


from django.conf.urls import url
from django.contrib import admin
from api import views

urlpatterns = [
    url(r'^drf/info/', views.DrfInfoView.as_view()),
]
#as_view()执行as_view方法和函数一样执行

4.写视图上

然后进行引用 模块

内置response代替了 django的jsonresponse

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from hula import models


# Create your views here.

class DrfCategoryView(APIView):
    def post(self, request, *args, **kwargs):
        #接收 有名传参(关键字传参)  无名传参(位置传参)
        obj = request.POST.get('data')
        if obj:
            models.Category.objects.create(**obj)
            return Response('OK成功')

知识点总结

resful规范

#基于cbv操作 也就是视图类
1.给别人提供一个URL,根据URL请求方式的不同,做不同操作。
    get,获取
    post,增加
    put,全部更新
    patch,局部更新
    delete,删除
2.数据传输基于json格式。
潜规则
因为不论什么请求方式,都需要给前端返回对象内容,就是json格式的
类的约束
如果出现 NOTmplementedError错误 说明必须实现实现发
方法

drf框架

不基于drf也可以是实现resful规范来开发接口程序
使用drf之后,可以快速帮我们开发resful规范来开发接口

初始化数据库

1.迁移数据库

外键需要使用为空或者给一个默认值
blank=True Null=True

2.url路由


from django.conf.urls import url
from django.contrib import admin
from api import views
urlpatterns = [
    url(r'^drf/category/', views.DrfCategoryView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
class DrfCategoryView(APIView):
    pass

3.FBV

接口:访问接口时,创建一个文章类型(post增加)

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from hula import models


# Create your views here.

class DrfCategoryView(APIView):
    def post(self, request, *args, **kwargs):
        #接收 有名传参(关键字传参)  无名传参(位置传参)
        obj = request.POST.get('data')
        if obj:
            models.Category.objects.create(**obj)
            return Response('OK成功')

假设写后端

4.开发完毕告诉前端访问

http://127.0.0.1:8000https://img.qb5200.com/download-x/drf/category/

5.模拟前端访问数据下载软件postman

默认使用x-www-urlencoded

拼接成 value&value

requesr.body获取请求体
request.POST获取的是POST请求的所有数据 
#字典类型 
#QueryDict: {'title': ['摩擦'], 'id': ['1']}>

request.body: name=alex&age=19&gender=12
request.POST: {'name': ['alex'], 'age': ['19'], 'gender': ['12']}

使用raw原生json发送

json(一般使用json)

注意点
#request.body获取是bytes类型
request.body: b'{"id":1,"name":"ALEX","AGE"}'
1.decode解码成json字符串
2.loads成可以给python处理的字典(json序列化)
    json.loads(request.body.decode('utf-8'))  
#json格式字符串是双引号
前端json的值
requesr.body获取请求体里面的数据
request.POST获取的是POST请求的所有数据 
request.body: b'{"ID":1,"name":"Alex","age":19}'
request.POST: 没有值

drf使用

对象类型转换成字典类型

from django.forms import model_to_dict
model_to_dict#转换
obj = models.Category.objects.filter(pk=pk).first()
            obj=model_to_dict(obj)

默认封装了 request.data(django没有) 进行了解码序列化

视图 APIview
request.data中有一个解析器
解析器,根据用户请求体格式不同进行数据解析,解析之后方法
在进行解析时候,drf会读取http

路径

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^info/$', views.InfoView.as_view()),
    url(r'^drf/info/$', views.DrfInfoView.as_view()),
    url(r'^drf/category/$', views.DrfCategoryView.as_view()),
    url(r'^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()),
]

直接全部文章get

class DrfCategoryView(APIView):
    def get(self,request,*args,**kwargs):
        #obj=models.Article.objects.values()
        #obj=models.Article.objects.all()
        # obj=models.Article.objects.all.values('id','name')
        obj=list(obj)
        return Response(obj)

?format=json 可以直接序列化json

单独的文章发送

不使用序列化器

from django.forms import model_to_dict
model_to_dict把model对象转换成字典

    def get(self,request,*args,**kwargs):
        pk=kwargs.get('pk')
        # print(1)
        if not pk:#判断有没有取到值
            obj=models.Article.objects.values()
            # obj=models.Article.objects.all.values('id','name')
            obj=list(obj)
            return Response(obj)
        else:
            print(1)
            obj = models.Article.objects.filter(pk=pk).first()
            obj=model_to_dict(obj)
            return Response(obj)

删除delete

 from api import models
from django.forms.models import model_to_dict
class DrfCategoryView(APIView):   
    def delete(self,request,*args,**kwargs):
        """删除"""
        pk = kwargs.get('pk')
        models.Category.objects.filter(id=pk).delete()
        return Response('删除成功')

更新put

知识点

request.data 可以进行序列化和解码
序列化和编码过程
json格式字符串是双引号
request.body: b'{"id":1,"name":"ALEX","AGE"}'
先进行解码
json序列化
json.loads(request.body.decode('utf-8'))
request.data可以接受json 和 &的数据

视图写法



from api import models
from django.forms.models import model_to_dict
class DrfCategoryView(APIView):

 def put(self,request,*args,**kwargs):
        """更新"""
        pk = kwargs.get('pk')
        models.Category.objects.filter(id=pk).update(**request.data)
        return Response('更新成功')

增加post

from api import models
from django.forms.models import model_to_dict
class DrfCategoryView(APIView):    
    def post(self,request,*args,**kwargs):
        """增加一条分类信息"""
        #利用model对象之间点create 需要一条一条添加
        #因此需要打散字典变成关键字形式
        models.Category.objects.create(**request.data)
        return Response('成功')

序列化器serializer

查看正确的值
is.vild()
    print(ser.validated_data)

多种情况使用多个serileiter进行区分 随机应变
是一个类
#知识点
1.model 指定哪一个model
2.fields 表示需要序列化的字段,"__all__"全部字段
3.depth 向下查找一层。指对外键关系会继续遍历外键对象的全部属性。
category=serializers.CharField(source='get_字段_display',required=False)
source可以自动查询是不是可执行的 自动加括号()

展示特殊的数据(choices、FK、M2M)可使用
depth source,无需加括号,在源码内部会去判断是否可执行,如果可执行自动加括号。【fk/choice】 SerializerMethodField,定义钩子方法。【m2m】

知识点

data=request.data 指定关键字传参 把提交的数据增加到数据库中
from rest_framework import serializers
class NewCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        # fields = "__all__"
        fields = ['id','name']
     def get_date(self,obj):
        return obj.date.strftime('%Y-%m-%d %H:%M')
    def post(self,request,*args,**kwargs):
        ser = NewCategorySerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)
    

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get("pk")
        if pk:
            obj=models.Article.objects.filter(pk=pk).delete()
            print(obj)
            return Response('删除成功')
        return Response('删除失败')

    def put(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        category_object = models.Category.objects.filter(id=pk).first()
        ser = NewCategorySerializer(instance=category_object,data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

知识点

1.ser.data可以把quest类型变为字典
ser.data
2.序列化对象 instance=对应的旧值 
3.many=true允许查询多个表必须指定的值
NewCategorySerializer(instance=queryset,many=True)

具体查询写法

class NewCategoryView(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        #多条数据查询
        if not pk:
            queryset = models.Category.objects.all()
            ser = NewCategorySerializer(instance=queryset,many=True)
            return Response(ser.data)
        else:
            #单条数据查询
            model_object = models.Category.objects.filter(id=pk).first()
            ser = NewCategorySerializer(instance=model_object, many=False)
            return Response(ser.data)

引用类似局部钩子

1.model 指定哪一个model
2.fields 表示需要序列化的字段,"__all__"全部字段
3.depth 向下查找一层。指对外键关系会继续遍历外键对象的全部属性。
category=serializers.CharField(source='get_字段_display',required=False)
source可以自动查询是不是可执行的 自动加括号()

展示特殊的数据(choices、FK、M2M)可使用
depth 
source,无需加括号,在源码内部会去判断是否可执行,如果可执行自动加括号。
【fk/choice】 SerializerMethodField,定义钩子方法。【m2m】

#datetime数据类型的显示
定义一个钩子
date=serializers.SerializerMethodField()
def get_date(self,obj):
    return obj.date.strftime(%Y-%m-%d %H:%M:%S) if obj.date else ""

获取外键对应的字段值

from rest_framework import serializers
from api import models
使用这个
class ArticleSerializer(serializers.ModelSerializer):
source='属性.跨表字段'
#名字可以写成和model字段一样用于覆盖 字段名对应的值
category=serializers.CharField(source='category.name',required=False)
 class Meta:
        model = models.Article
        fields = "__all__"
        #如果要写fields="__all__"
        #需要指定字段 是从获取字段名对应的值
def get_x1(self,obj):
    return obj.category.name
#查出来对象 可以点属性
source可以自动查询是不是可执行的 自动加括号()

获取choice选择框对应值

获取choice选择框对应值
from rest_framework import serializers
from api import models
获取对应的列表的名称
class ArticleSerializer(serializers.ModelSerializer):
#第一种
status_txt = serializers.CharField(source='get_status_display',required=False)
#第二种
x2 = serializers.SerializerMethodField()#类似一个钩子
 def get_x2(self,obj):
     return obj.get_status_display()

#跨表
class ArticleSerializer(serializers.ModelSerializer):
 x1 = serializers.SerializerMethodField()
     class Meta:
        model = models.Article
        # fields = "__all__"
        fields = 
['id','title','summary','content','category','category_txt','x1','status','status_txt','x2']
#如果放入fields 
#如果不写required=False或者read_onlye=False 会进行判断不为空

def get_x1(self,obj)
    #obj当前表的对象
    #return 对象.字段属性.跨表字段
    return obj.category.name

多对多
SerializerMethodField,定义钩子方法。【m2m】
class NewArticleSerializer(serializers.ModelSerializer):    tag_info = serializers.SerializerMethodField()    class Meta:        
        model = models.Article        
        fields = ['title','summary','tag_info']
        #钩子取出每一个字典
        def get_tag_info(self,obj):        
            return [row for row in obj.tag.all().values('id','title')]
        class FormNewArticleSerializer(serializers.ModelSerializer):    class Meta:        
                model = models.Article       
                fields = '__all__'

要想只更新一个字段

使用patch

partial=True#允许部分更新
partial=True
def patch(self,request,*args,**kwargs):
        """局部"""
        pk = kwargs.get('pk')
        article_object = models.Article.objects.filter(id=pk).first()
        ser = serializer.ArticleSerializer(instance=article_object, data=request.data,partial=True)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

多对多关系

NewArticleSerializer写法

使用钩子

class NewArticleSerializer(serializers.ModelSerializer):    
    tag_info = serializers.SerializerMethodField()    
    class Meta:        
        model = models.Article        
        fields = ['title','summary','tag_info']
    def get_tag_info(self,obj):        
        return [row for row in obj.tag.all().values('id','title')]
class FormNewArticleSerializer(serializers.ModelSerializer):    
    class Meta:        
        model = models.Article        
        fields = '__all__'

视图类写法

    class NewArticleView(APIView):
    def get(self,request,*args,**kwargs):        
        pk = kwargs.get('pk')        
        if not pk:            
            queryset = models.Article.objects.all()            
            ser = serializer.NewArticleSerializer(instance=queryset,many=True)            
            return Response(ser.data)        
        article_object = models.Article.objects.filter(id=pk).first()        
        ser = serializer.NewArticleSerializer(instance=article_object, many=False)        
        return Response(ser.data)
    def post(self,request,*args,**kwargs):        
        ser = serializer.FormNewArticleSerializer(data=request.data)        
        if ser.is_valid():            
            ser.save()            
            return Response(ser.data)        
        return Response(ser.errors)
    def put(self, request, *args, **kwargs):        
        """全部更新"""        
        pk = kwargs.get('pk')        
        article_object = models.Article.objects.filter(id=pk).first()        
        ser = serializer.FormNewArticleSerializer(instance=article_object, data=request.data)               if ser.is_valid():        
            ser.save()            
            return Response(ser.data)        
        return Response(ser.errors)
    def patch(self,request,*args,**kwargs):        
        """局部"""        
        pk = kwargs.get('pk')        
        article_object = models.Article.objects.filter(id=pk).first()        
        ser = serializer.FormNewArticleSerializer(instance=article_object,data=request.data,partial=True)        
        if ser.is_valid():            
            ser.save()            
            return Response(ser.data)        
        return Response(ser.errors)
    def delete(self,request,*args,**kwargs):       
        pk = kwargs.get('pk')        
        models.Article.objects.filter(id=pk).delete()        
        return Response('删除成功')

分页

PageNumberPagination

用于固定页面显示

获取多条数据

知识点

    "count": 54,#每页显示多少条数据
        #下一页
    "next": "http://127.0.0.1:8000https://img.qb5200.com/download-x/drf/article/?page=2",
        #上一页 
    "previous": null,

类的约束

必须写指定的功能
# 约束子类中必须实现f1 class Base(object):    
def f1(self):        
    raise NotImplementedError('asdfasdfasdfasdf')
class Foo(Base):        
    def f1(self):        
        print(123)        
obj = Foo() 
obj.f1()

1.可以重写pagesize的类 pagenumber 继承

引用模块
from rest_framework.pagination import PageNumberPagination#引用分页模块
from rest_framework import serializers#引用序列化模块

class pagesize(PageNumberPagination):
    page_size=1

    class PageArticleView(APIView):
        def get(self,request,*args,**kwargs):
            queryset=models.Article.objects.all()
            page_obj = pagesize()#实例化一个页面大小
            #进行分页
            result = page_obj.paginate_queryset(queryset, request, self)
            #把分页的数据写入序列化器
            ser = PageArticleerializer(instance=result, many=True)
            return page_obj.get_paginated_response(ser.data)

2.直接更改setting配置文件

REST_FRAMEWORK = {
    "PAGE_SIZE":2
}
class PageArticleView(APIView):
    def get(self,request,*args,**kwargs):
        queryset=models.Article.objects.all()
        page_obj=PageNumberPagination()
        result=page_obj.paginate_queryset(queryset,request,self)
        ser=PageArticleerializer(instance=result,many=True)
        return Response(ser.data)

LimitOffsetPagination

offset 0 limit 1
从offset开始数limit条数
用于滑动灵活运用
可以限制max_limit=2
from rest_framework.pagination import PageNumberPagination 
from rest_framework.pagination import LimitOffsetPagination 
from rest_framework import serializers
class PageArticleSerializer(serializers.ModelSerializer):    
    class Meta:        
        model = models.Article        
        fields = "__all__"
        #重写父类
class HulaLimitOffsetPagination(LimitOffsetPagination):    
    max_limit = 2     
class PageArticleView(APIView):    
       def get(self,request,*args,**kwargs):        
            queryset = models.Article.objects.all()
            page_object = HulaLimitOffsetPagination()        
            result = page_object.paginate_queryset(queryset, request, self)        
            ser = PageArticleSerializer(instance=result, many=True)        
            return Response(ser.data)

listapiview

url写法

url(r'^page/view/article/$', views.PageViewArticleView.as_view()),

视图写法

from rest_framework.generics import ListAPIView
class PageViewArticleSerializer(serializers.ModelSerializer):    
    class Meta:        
        model = models.Article        
        fields = "__all__"
class PageViewArticleView(ListAPIView):    
    queryset = models.Article.objects.all()  
    #指定类
    serializer_class = PageViewArticleSerializer

setting配置

#通过源码里面的配置进行分页的选择
REST_FRAMEWORK = {    "PAGE_SIZE":2,    
                  #分页的选择
                  "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination" 
                 }

呼啦圈的设置

CMS系统格式

内容管理系统。CMS通常用作企业的数字信息管理系统,

功能的实现

1 增加文章(可以不写) #编写人员进行撰写 写入数据库
2 文章列表 
3 文章详细
4 评论列表
1.没有变化的类型不需要创建一张表
2.数据量大的类似详细内容 需要另外开辟一张表,如果表中列(字段)太多 水平分表 进行一对一
3.图片路径存入数据库
4.自关联具有相同数据的表

#不是必须的
可以不需要写外键提高查询效率

1.url路径


urlpattent+=[]#为了区分增加的路径
from django.conf.urls import url
from api import views
urlpatterns = [
    url(r'^Atricle/$', views.AtricleView.as_view()),
]
urlpatterns+=[
    url(r'^Atricle/(?P<pk>\d+)/$', views.AtricleView.as_view()),
    url(r'^Comment/$', views.CommentView.as_view()),
    url(r'^Comment/(?P<pk>\d+)/$', views.CommentView.as_view()),
]

2.model 表结构

from django.db import models

class UserInfo(models.Model):
    """ 用户表 """
    username = models.CharField(verbose_name='用户名',max_length=32)
    password = models.CharField(verbose_name='密码',max_length=64)


class Article(models.Model):
    """ 文章表 """
    category_choices = (
        (1,'咨询'),
        (2,'公司动态'),
        (3,'分享'),
        (4,'答疑'),
        (5,'其他'),
    )
    category = models.IntegerField(verbose_name='分类',choices=category_choices)
    title = models.CharField(verbose_name='标题',max_length=32)
    image = models.CharField(verbose_name='图片路径',max_length=128) # /media/upload/....
    summary = models.CharField(verbose_name='简介',max_length=255)

    comment_count = models.IntegerField(verbose_name='评论数',default=0)
    read_count = models.IntegerField(verbose_name='浏览数',default=0)

    author = models.ForeignKey(verbose_name='作者',to='UserInfo')
    date = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)

class ArticleDetail(models.Model):
    article = models.OneToOneField(verbose_name='文章表',to='Article')
    content = models.TextField(verbose_name='内容')


class Comment(models.Model):
    """ 评论表 """
    article = models.ForeignKey(verbose_name='文章',to='Article')
    content = models.TextField(verbose_name='评论')
    user = models.ForeignKey(verbose_name='评论者',to='UserInfo')
    # parent = models.ForeignKey(verbose_name='回复',to='self', null=True,blank=True)

3.serlizer序列化器

知识点补充

1.#序列化器写法
exclude=['字段']去除不进行校验
2.#序列化器内部的值
ser是序列化器实例化的对象
ser=序列化器(
    1.instance=旧值(查询的值,数据库本类就有的)
    2.data=要添加的值,提交的值(post请求,put,patch请求提交的值)   )
    3.查询的时候 需要指定many=True 查询多个值必须要指定
ser.is_valid():
    '''
    注意只打印存在字段的校验,
    传入多的值不会进行打印和数据库的写入
    ser.errors是所有字段校验错误的信息
    '''
    print(ser.validated_data)#相当于modelform form校验的值
    ser.save()#存入数据库 括号里可以写值 由其他字段传入的
    #article=ser.save()#aricle是新增这条数据的对象

多个序列化器

接收前端发送的所有数据但只校验 序列化器含有的字段

ser=AtricleSeriALIZER(data=request.data)
ser_detail=ArtcleDetaili(data=request.data)
if ser.is_valid() and ser_detail.is_valid()
article=ser.save(author=1)
ser_detail.save(article=article)
#可以等于对象=对象
#也可等于id=id(内容回顾)
ser_detail.save(article.id=article.id)

```

4.view视图写法

post请求

(编写数据的人员去写)

知识点
1.多个serlizer序列化器进行保存
2.save里面可以添加参数 
    可以添加对应字段的id和对象类型的数据
    没有办法直接添加外键对应的值 可以通过save(对应的值或者对象)
3.序列化的对象就是对应文章的对象
4.作者是登陆成功存入session的值 不需要手动传入
序列化器写法
exclude=['author']#去除作者不进行校验
#为了把字段写活request.session获取值进行匹配取得匹配的文章
#查询对应文章对象不能直接post写死 因为不知道对应的文章
class AtricleSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Article
        exclude = ['author', ]#外键字段不进行校验
        #request.session 登陆用户明进行匹配

        class AtricledetailSerializer(serializers.ModelSerializer):
            class Meta:
                model = models.ArticleDetail
                exclude = ['article', ]
                #外键字段不进行校验
    2#文章对象或者文章值来确定 来确定添加对应的文章详细信息

view视图写法

两个序列化器进行校验

    def post(self, request, *args, **kwargs):
        ser = AtricleSerializers(data=request.data)
        serDetail = AtricledetailSerializer(data=request.data)
        print(request.data)
        if ser.is_valid() and serDetail.is_valid():
            # 因为作者id是根据登陆的id进行存储(例如session)
            print(ser.validated_data)
            atricle = ser.save(author_id=1)
            serDetail.save(article=atricle)
            return Response(f'{ser.data}{serDetail.data}')
        return Response('错误')

文章列表显示排序

先添加在显示

序列化写法

class AtiricleliST(serializers.ModelSerializer):
    class Meta:
        model = models.Article
        fields = "__all__"
很多文章的获取get请求
多条数据
1.先进行实例化分页
2.把查询的数据进行分页,
3.放入序列化器里面进行处理显示    
    def get(self, request, *args, **kwargs):
        pk=kwargs.get("pk")
        if not pk:
            Article_obj = models.Article.objects.all().order_by('-date')
            Page_obj = PageNumberPagination()
            Page = Page_obj.paginate_queryset(Article_obj, request, self)
            print(Page)
            ser = AtiricleliST(instance=Page, many=True)
            print(3)
            return Page_obj.get_paginated_response(ser.data)
        

文章筛选

前端的url
全部:http://127.0.0.1:8000/hg/article/
筛选:http://127.0.0.1:8000/hg/article/?category=2
视图
#筛选文章的数据
class ArticleView(APIView):
    """ 文章视图类 """

    def get(self,request,*args,**kwargs):
        """ 获取文章列表 """
        pk = kwargs.get('pk')
        if not pk:
            condition = {}#存储对应筛选
            category = 
             #从前端url获取值
            request.query_params.get('category')
            if category:#如果由就进行过滤
                condition['category'] = category
            queryset = 
           #**condition 字典打散然后进行筛选 分类为这个id的
        models.Article.objects.filter(**condition).order_by('-date')
            
            pager = PageNumberPagination()
            result = pager.paginate_queryset(queryset,request,self)
            ser = ArticleListSerializer(instance=result,many=True)
            return Response(ser.data)
        article_object = models.Article.objects.filter(id=pk).first()
        ser = PageArticleSerializer(instance=article_object,many=False)
        return Response(ser.data)

文章详细

#去除对应的外键关联字段的验证article
#只有文章创建了之后才能创建文章详情
#author对应的是用户id值,例如session


#因为文章详细不能直接创建需要通过文章对象关联创建
class AtricledetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.ArticleDetail
        exclude = ['article', ]

class AtricleSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Article
        exclude = ['author', ]

查询一条数据的详细

view视图写法

def get(self, request, *args, **kwargs):
        pk=kwargs.get("pk")
        if not pk:
            Article_obj = models.Article.objects.all().order_by('-date')
            Page_obj = PageNumberPagination()
            Page = Page_obj.paginate_queryset(Article_obj, request, self)
            print(Page)
            ser = AtiricleliST(instance=Page, many=True)
            print(3)
            return Page_obj.get_paginated_response(ser.data)
        #正文开始 else是一条 if是多条
else:
            Article_obj=models.Article.objects.filter(pk=pk).first()
 #使用序列化处理一条数据
ser=AtiricleDetail(instance=Article_obj,many=False)
            return Response(ser.data)



·································
文章详细 文章表反向小写表名查询详细表 
Article_obj=models.Article.objects.filter(pk=pk).first()
属性.小写表名.外键关联表属性
print(Article_obj.articledetaili.content)

评论

1.访问get请求

http://127.0.0.1:8000/hg/comment/?article=2
request.query_params.get('article') 
#相当于request.GET。get 获取url的值
query_params获取?后面值
reqeust.data 相当于post
序列化器
class MmentSerialiser(serializers.ModelSerializer):
    class Meta:
        model=models.Comment
        fields="__all__"
view视图
    通过前端发送url进行筛选
   # http://127.0.0.1:8000/hg/comment/?article=2
    def get(self,request,*args,**kwargs):
        '''
        request.query_params.get()
        相当于request.GET.get()
        '''

        coment=request.query_params.get('article')
        coment_obj=models.Comment.objects.filter(article_id=coment)
        show_coment=MmentSerialiser(instance=coment_obj,many=True)
        return Response(show_coment.data)
2.添加文章评论post请求
#去掉对应外键字段硬不需要进行关联
#request.session可以直接获取登陆的用户名
class CommentSerialiser(serializers.ModelSerializer):
    class Meta:
        model=models.Comment
        exclude=['user']

锁定单个文章的评论增加

前端写法
#返回数据的写法
#第一种
http://127.0.0.1:8000/hg/comment/

{
    article:1,
    content:'xxx'
}
#第二种
http://127.0.0.1:8000/hg/comment/?article=1

{
    content:'xxx'
}
view视图写法
 #文章由前端直接返回因为只有有了文章才能进行评论
    #通过前端数据进行返回
    
    def post(self,request,*args,**kwargs):
        ser=CommentSerialiser(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
            ser.save(user_id=1)
            return Response(ser.data)
        print(ser.errors)

过滤器

对查询出来的数据进行筛选可写可不写

from rest_framework.filters import BaseFilterBackend
源码
'''
    def filter_queryset(self, request, queryset, view):
    #继承了这个类必须写这个方法不然报错
        raise NotImplementedError(".filter_queryset() must be overridden.")
'''
view是当前视图self
queryset是筛选之后的数据
request 就是request
def filter_queryset(self,request,queryset,view):
    pass

使用方法

对查询出来的数据进行筛选可写可不写
#第一部分
from rest_framework.filters import BaseFilterBackend
#继承类
class MyFilterBack(BaseFilterBackend):
def filter_queryset(self,request,queryset,view):
    val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)

#先实例化一个
class Indexview(APIview):
    def get(self,request,*arg,**kwargs):
        #就是查询一个表不理他
        queryset=models.News.objects.all()
        #实例化一个类对象
        obj=MyFilterBack()
 #传值顺序request,queryset,self
result=obj.filter_queryset(request,queryset,self)
        return Response('...')

视图类

F查询

 def get(self,request,*args,**kwargs):
        # 1.获取单挑数据再做序列化
        result = super().get(request,*args,**kwargs)
        # 2.对浏览器进行自加1
        pk = kwargs.get('pk')
        models.Article.objects.filter(pk=pk).update(read_count=F('read_count')+1)
        # 3.对浏览器进行自加1
        # instance = self.get_object()
        # instance.read_count += 1
        # instance.save()
        return result

apiview

提供了公共方法
request.data 是request.post的封装 json序列化
dispatch()分发
self封装了request属性
self.request.method

源码剖析

 @classmethod
    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
        #执行父类的as_view方法
        view = super().as_view(**initkwargs)
        #执行父类的super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        #闭包 csrf_exempt免除csrftoken的认证
        return csrf_exempt(view)
    
    
    
--------------------------------------    
    
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            #执行本类的self.dispatch
            return self.dispatch(request, *args,**kwargs) 
        #执行view
        return view
  
-------------apiview的dispatch--------
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #封装老值request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response



GenericAPIView

#一 初识
from rest_framework.generics import GenericAPIView
GenericAPIview继承了APIview
class NewView(GenericAPIview):
    querset=model.News.objects.all()
    def get(self,request,*arg,**kwargs):
        #self对应NewView的对象
        self.filter_queryset(self.queryset)
1.执行对应的filter_queryset方法
2.本类没有去寻找父类GenericAPIview
'''
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

'''#所以默认返回原来的queryset
3.寻找filter_backends
'''
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    filter_backends是空的
'''

1. 本类重写filter_backends

from rest_framework.generics import GenericAPIView
#GenericAPIview继承了APIview
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        def get(self,request,*arg,**kwargs):
            #self对应NewView的对象
            v=self.get_queryset()
          queryset=self.filter_queryset(v)
 1.执行对应的filter_queryset方法
2.本类没有去寻找父类GenericAPIview
'''
源码 继承类不写self.queryset会报错
assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
'''

'''
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

'''4.backend等于对应NewFiltrBackend类名() 实例化对象
··  执行NewFiltrBackend里面的filter_queryset方法
3.寻找filter_backends,本类的filter_backends
    #filter_backends=[NewFiltrBackend ,]
5.queryset=self.filter_queryset(self.queryset)是筛选之后的结果
#v=self.get_queryset()
queryset=self.filter_queryset(v)
6.寻找对应get_queryset
'''
源码
 queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset
        
'''#返回等于筛选之后的queryset
    #queryset=self.filter_queryset(queryset)
#queryset=get_queryset()
·········self.get_serializer()··············· 
class Newserializers(serializers.ModelSerializer):
    class Meta:
        model=models.News
        fields="__all__"
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        def get(self,request,*arg,**kwargs):
            #self对应NewView的对象
            v=self.get_queryset()
            queryset=self.filter_queryset(v)
            self.get_serializer()#本类没有去父类找
            #代替了                                    ser=Newserializers(instance=queryset,many=True)
            #ser.data
            
  1.寻找get_serializer()
2.
'''
源码
    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)
'''
3.进行寻找get_serializer_class()本类没有去父类找
#serializer_class = self.get_serializer_class()
4.
'''
    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class
''' #return self.serializer_class 返回serializer_class
#get_serializer()=serializer_class
在本类定义seializer_class
seializer_class=Newserializers

相当于                                     #ser=Newserializers(instance=queryset,many=True)
#ser.data
ser=self.get_serializer(instance=queryset,many=True)
ser.data

分页
querset=model.News.objects.all()
pagination_class =PageNumberPagination
self.paginate_queryset(queryset)
'''
源码
self.paginate_queryset()

self._paginator = self.pagination_class()
需要定义
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator

'''pagination_class()需要本地定义
pagination_class =PageNumberPagination
````

GenericAPIView源码总结

    源码的剖析
    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset
源码解释
from rest_framework.generics import GenericAPIView
from rest_framework.filters import BaseFilterBackend
from rest_framework import serializers
GenericAPIview继承了APIview
class Newserializers(serializers.ModelSerializer):
    class Meta:
        model=models.News
        fields="__all__"
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        seializer_class=Newserializers
        def get(self,request,*arg,**kwargs):
            #self对应NewView的对象
            v=self.get_queryset()
            queryset=self.filter_queryset(v)
     self.get_serializer(instance==queryset,many=True)
     retutn Response(ser.data)
 '''
        1.querset=model.News.objects.all()
        2.filter_backends=[NewFiltrBackend ,]
        3.seializer_class=Newserializers
        4.pagination_class =PageNumberPagination
 1.查询   
 self.get_queryset()#等于querset=model.News.objects.all()
 2.序列化
 self.get_serializer()等于seializer_class=Newserializers()
 3.筛选
 self.filter_queryset()
 等于filter_backends=[NewFiltrBackend ,]
 内部有一个for循环列表 同等与NewFiltrBackend()
 4.分页
 self.paginate_queryset(queryset)
 等于page_obj=PageNumberPagination()
 '''

基于GenericAPIView

listapiview查看

基于GenericAPIView

模拟get请求

1.queryset

2.分页


#settings的配置
paginator.paginate_queryset(queryset, self.request, view=self)
"DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
    
"PAGE_SIZE":2,

3.序列化

4.返回序列化数据

源码剖析

 #源码
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
1.执行list
     def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
#self.get_queryset()执行自定的
#queryset=model.News.objects.all()
#self.filter_queryset()筛选执行
#filter_backends=[NewFiltrBackend ,]
#执行对应本类的filter_queryset
       2. page = self.paginate_queryset(queryset)
    #分页
    #执行 pagination_class =PageNumberPagination
        if page is not None:
          3.serializer = self.get_serializer(page, many=True)
        #序列化 执行seializer_class=Newserializers
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
    #最后返回序列化的数据

RetrieveAPIView单条查看

模拟get请求

执行流程

1. #对应获得对象 自己要定义单条数据 queryset
2.    #序列化
3.     #返回数据

源码剖析

class RetrieveAPIView(mixins.RetrieveModelMixin,
                      GenericAPIView):  
    #从左往右继承
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    #去找retrieve这个方法
class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        #对应获得对象
        instance = self.get_object()
        #序列化
        serializer = self.get_serializer(instance)
        #返回数据
        return Response(serializer.data)
    
    执行
    
     def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        return obj

基于GenericAPIView

createapiview创建

基于GenericAPIView

封装了post请求

执行流程

1.序列化
2.序列化验证
3.保存数据

源码剖析

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
    
    1.执行create
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        #序列化
        serializer.is_valid(raise_exception=True)
        #序列化验证
        self.perform_create(serializer)
        '''
        保存数据
         def perform_create(self, serializer):
        serializer.save()
        '''
           
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

示例使用

#第一种写法重写create书写 多条序列化器
    def create(self, request, *args, **kwargs):

        article = articleseralizer(data=request.data)
        articleDetail = atricleDetaliser(data=request.data)
        if article.is_valid() and articleDetail.is_valid():
            article_obj = article.save(author=request.user)
            print(">>>>>>>>>", request.user, type(request.user))
            articleDetail.save(article=article_obj)
            return Response('修改成功')
        else:
            return Response(f"{article.errors},{articleDetail.errors}")
        
        
#第二种方式
重写序列化器分发
def get_serializer_class(self):
    self.request.method=="POST":
    return 对应序列化器
    self.request.method=="GET":
    return 对应序列化器
def perform_create(self,serializer):
    Aritlel=serializer.save(author)
    serializer_second=AricleSerializer(data=request.data)
    serializer.save(aritle=Aritlel)
    字段对象=对象
    
    

UpdateAPIView全局局部

封装了局部更新和全局更新

基于GenericAPIView

执行流程

1.获取更新那条数据的对象
2.序列化
2.序列化校验
3.校验成功更新

源码剖析

    
     def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    '''
     def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        '''
        #执行queryset查询处理的model对象
#相当于
#queryset=model.表名.object.filter(pk=pk).frist()
'''
        serializer = self.get_serializer(instance, data=request.data, partial=partial)默认不为Ture
        #执行对应的序列化
        serializer.is_valid(raise_exception=True)
        #序列化验证
        self.perform_update(serializer)
     '''
        # def perform_update(self, serializer):
            #serializer.save()
        
    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
    '''
        def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    '''
执行
 '''
     def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        '''
        #执行queryset查询处理的model对象
#相当于
#queryset=model.表名.object.filter(pk=pk).frist()
'''
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        kwargs['partial'] = True 局部更新
        #执行对应的序列化
        serializer.is_valid(raise_exception=True)
        #序列化验证
        self.perform_update(serializer)
     '''
        # def perform_update(self, serializer):
            #serializer.save()

删除DestroyAPIView

执行流程

1.传入对象  
2.执行def perform_destroy(self, instance):
3.删除

源码剖析

 def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            #c传入对象删除
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()#删除

Viewset视图类

是APIview视图类的继承对象

createapiview
UpdateAPIview。。。。等等

使用方法

1.直接继承即可
2.因为没有封装对应get post等请求方法 一点点去找父类的方法
3.GenericViewSet(ViewSetMixin)它继承的ViewSetMixin
4.重写了asview()需要传参
5.使用两个类进行id和没有id的区分
url(r'^article/$',article.AtricleView.as_view({"get":"list"}))
url(r'^article/(?P<pk>\d+)/$',article.AtricleView.as_view({"get":"retrieve"}))

知识点

from rest_framework.viewsets import GenericVIewSet
class GenericViewSet(ViewSetMixin,generics.GenericAPIView
                    ):
默认继承GenericAPIView
    def get_serializer_class(self):
        pk = self.kwargs.get('pk')
        if pk:
            return ArticleDetailSerializer
        return ArticleSerializer

ListModelMixin添加

源码

 def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

RetrieveModelMixin单条查询

源码


    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

UpdateModelMixin更新

源码

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

        #
    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    class 
    

DestroyModelMixin:

删除

源码

class DestroyModelMixin:
"""
    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()

版本

setting配置

    default_version = api_settings.DEFAULT_VERSION
    #默认版本
    allowed_versions = api_settings.ALLOWED_VERSIONS
    #指定版本
    version_param = api_settings.VERSION_PARAM
    #url版本传输关键字
      #指定版本类类
 "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",

知识点

dispath 老的request 封装了很多功能

request.version#版本
 scheme#版本对象
#执行返回一个版本元组 第一个是版本第二个是版本对象
 def determine_version(self, request, *args, **kwargs):
        """
        If versioning is being used, then determine any API version for the
        incoming request. Returns a two-tuple of (version, versioning_scheme)
        """
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme) 
        request.version, request.versioning_scheme = version, scheme

源码执行流程


class APIView(View):
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
    def dispatch(self, request, *args, **kwargs):
       
        # ###################### 第一步 ###########################
        """
        request,是django的request,它的内部有:request.GET/request.POST/request.method
        args,kwargs是在路由中匹配到的参数,如:
            url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()),
            http://www.xxx.com/order/1/v2/
        """
        self.args = args
        self.kwargs = kwargs

        """
        request = 生成了一个新的request对象,此对象的内部封装了一些值。
        request = Request(request)
            - 内部封装了 _request = 老的request
        """
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

        self.headers = self.default_response_headers  # deprecate?

        try:
            # ###################### 第二步 ###########################
            self.initial(request, *args, **kwargs)

            执行视图函数。。
    
    def initial(self, request, *args, **kwargs):
       
        # ############### 2.1 处理drf的版本 ##############
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        ...
        
    def determine_version(self, request, *args, **kwargs):
        if self.versioning_class is None:
            return (None, None)
        #是版本类的实例化对象
        scheme = self.versioning_class() 
        # obj = XXXXXXXXXXXX()
        return (scheme.determine_version(request, *args, **kwargs), scheme)
        
class OrderView(APIView):
    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
        print(request.versioning_scheme)
        return Response('...')

    def post(self,request,*args,**kwargs):
        return Response('post')

1.局部配置

视图写法

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.versioning import URLPathVersioning


class OrderView(APIView):

    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
        print(request.versioning_scheme)
        return Response('...')

    def post(self,request,*args,**kwargs):
        return Response('post')

setting配置

 REST_FRAMEWORK = {
    "ALLOWED_VERSIONS":['v1','v2'],
    'VERSION_PARAM':'version'
 }

2.全局配置

不用手动一个个加全部都配置上

url

url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),

url(r'^(?P<version>\w+)/users/$', users_list, name='users-list'),

settings配置

REST_FRAMEWORK = { 
 指定版本类 必须要写要些路径   "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    #限制版本范围
    "ALLOWED_VERSIONS":['v1','v2'],
    #指定url有名分组的关键字
    'VERSION_PARAM':'version'
}

认证

自定义认证token
把数据库的user拿出来比较
如果想使用**request.data 就要把他变为字典 所以前端要返回json数据类型
源码写法
#配置
 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

局部配置

和登陆配合使用
import uuid
from django.shortcuts import render
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView
from rest_framework.response import Response
class Loginview(APIView):
    versioning_class = None
    #认证写法
    authentication_classes = [Myauthentication, ]

def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登录失败')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

class MyAuthentication:
    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        token = request.query_params.get('token')
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return (user_object,token)
        return (None,None)

class OrderView(APIView):
    authentication_classes = [MyAuthentication, ]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        return Response('order')

class UserView(APIView):
    authentication_classes = [MyAuthentication,]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        return Response('user')

    
    
    
···············手写认证类····························

class Myauthentication:
    # 认证
    # versioning_class = None
    #手写认证
    def authenticate(self, request):
        token = request.query_params.get('token')
        print(token)
        user_object = models.UserInfo.objects.filter(token=token).first()
        print(user_object)
        if user_object:
            print(1)
            #必须返回一个元组的形式
            #返回对应的user_object和token值
            
            return (user_object, token)
        else:
            print(2)
        return (None, None)
    
    
    
    
    
----------------------
    为什么要返回元组
----------------------
    #为什么要返回元组
  def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = 
                #到这返回为什么可以执行对应的方法
                authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
                
            if user_auth_tuple is not None:
                #把查询出来的元组
                self._authenticator = authenticator
                #给user和auth进行赋值
                self.user, self.auth = user_auth_tuple#必须返回一个元组的形式
                return#结束函数

        self._not_authenticated()

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None
        
        if api_settings.UNAUTHENTICATED_USER:
            #user 没有设置返回none
            self.user = 
            #匿名用户
            api_settings.UNAUTHENTICATED_USER()
        else:
            self.user = None
          
        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth =
            #对应author没有设置返回none
            api_settings.UNAUTHENTICATED_TOKEN()
        else:
            self.auth = None

全局配置

DEFAULT_AUTHENTICATION_CLASSES=['写一个认证的类手写的']
class Myauthentication:
    # 认证
    # versioning_class = None
    #手写认证
    def authenticate(self, request):
        token = request.query_params.get('token')
        print(token)
        user_object = models.UserInfo.objects.filter(token=token).first()
        print(user_object)
        if user_object:
            print(1)
            #必须返回一个元组的形式
            #返回对应的user_object和token值
            
            return (user_object, token)
        else:
            print(2)
        return (None, None)
        '''
          # raise Exception(), 不在继续往下执行,直接返回给用户。
        # return None ,本次认证完成,执行下一个认证
        # return ('x',"x"),认证成功,不需要再继续执行其他认证了,继续往后权限、节流、视图函数
        '''

view视图写法

class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登录失败')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

class OrderView(APIView):
    # authentication_classes = [TokenAuthentication, ]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        if request.user:
            return Response('order')
        return Response('滚')

class UserView(APIView):
    同上

源码执行流程

'''
    当请求发过来的实话会先执行 apiview里面的dispath方法
   1.执行认证的dispatch的方法
    2.dispatch方法会执行initialize_request方法封装旧的request对象
 
    3.在封装的过程中会执行
    get_authenticators(self): 并实例化列表中的每一个认证类
    如果自己写的类有对应的认证类 会把认证类的实例化对象封装到新的request当中
    继续执行initial 
    会执行认证里面的perform_authentication 执行request.user 
    会执行循环每一个对象执行对应的authenticate方法
    会有三种返回值
     # raise Exception(), 不在继续往下执行,直接返回给用户。
        # return None ,本次认证完成,执行下一个认证
        # return ('x',"x"),认证成功,不需要再继续执行其他认证了,继续往后权限、节流、视图函数
    
    
    
    里面会执行四件事
    版本
    认证
    权限
    节流
'''
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #封装旧的request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    循环自定义的认证
    #request封装旧的request执行initialize_request
    
        def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
    
    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        
        return [auth() for auth in self.authentication_classes]

        def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
    
    #执行完四件事在执行视图

权限

message=消息 自定义错误消息
可以自定义错误信息
'''
 for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
'''

本质上自定义和
return Flase
单条验证
验证两次 
不写url_kwarg默认封装识别pk关键字

重写错误信息的两种方式

第一种

message={"code":"恭喜你报错了"}

第二种

from rest_framework.permissions import BasePermission
from rest_framework import exceptions

class MyPermission(BasePermission):
    #第一种
    message = {'code': 10001, 'error': '你没权限'}
    def has_permission(self, request, view):
        #request.user执行认证
        if request.user:
            return True
    #自定义错误信息
         raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
        return False

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
    
    
#对应源码
相当于重写了错误信息
#执行顺序 
dispatch---》intital--》check_permissions--》permission_denied
    def permission_denied(self, request, message=None):
        """
        If request is not permitted, determine what kind of exception to raise.
        """
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise 
        #等于message 或者自定义错误函数
        #raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
        exceptions.PermissionDenied(detail=message)
        
        
 ················源码··················
  def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

源码剖析

执行流程
apiview
---
dispatch
---
initial
---
执行权限判断
如果自己写了权限类 会先循环权限类并实例化存在列表当中
然后循环列表对象,如果权限判断返回不为Ture机会进行主动抛出异常
可以自定义错误信息
具体代码
class APIView(View):
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    
    def dispatch(self, request, *args, **kwargs):
        封装request对象
        self.initial(request, *args, **kwargs)
        通过反射执行视图中的方法

    def initial(self, request, *args, **kwargs):
        版本的处理
        # 认证
        self.perform_authentication(request)
        
        # 权限判断
        self.check_permissions(request)
        
        
        self.check_throttles(request)
        #执行认证
    def perform_authentication(self, request):
        request.user
    
    def check_permissions(self, request):
        # [对象,对象,]
        self.执行
        '''
def get_permissions(self):
        return [permission() for permission in self.permission_classes]
        '''
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(request, message=getattr(permission, 'message', None))
    def permission_denied(self, request, message=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message)
        
    
# 
   
class UserView(APIView):
    permission_classes = [MyPermission, ]
    
    def get(self,request,*args**kwargs):
        return Response('user')

使用

class MyPermission(BasePermission):
    message = {'code': 10001, 'error': '你没权限'}
    def has_permission(self, request, view):
        #request.user执行认证
        if request.user:
            return True

         raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
        return False

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
    
    
 class UserView(APIView):
    先执行上面的数据
    permission_classes = [MyPermission, ]
    #再执行
    def get(self,request,*args**kwargs):
        return Response('user')   

源码流程

``python def dispatch(self, request, *args, **kwargs): """.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?

    try:
        #走这
        self.initial(request, *args, **kwargs)

`````````````````def initial``````````````````````````

def initial(self, request, *args, **kwargs):
    """
    Runs anything that needs to occur prior to calling the method handler.
    """
    self.format_kwarg = self.get_format_suffix(**kwargs)

    # Perform content negotiation and store the accepted info on the request
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg

    # Determine the API version, if versioning is in use.
    version, scheme = 
    '''
    第三步
    '''
    self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # Ensure that the incoming request is permitted
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)
    
    
    

第二步

def check_permissions(self, request):
# [对象,对象,]
self.执行
'''
def get_permissions(self):
return [permission() for permission in self.permission_classes]
'''
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(request, message=getattr(permission, 'message', None))
def permission_denied(self, request, message=None):
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
raise exceptions.PermissionDenied(detail=message)
```

第三步

    def determine_version(self, request, *args, **kwargs):
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)

单挑数据的验证

单挑数据

def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
#在执行RetrieveAPIView 会执行单挑数据的验证

    def get_object(self):
        
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        #不写url_kwarg默认封装识别pk关键字
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        #检验单挑数据的权限
        self.check_object_permissions(self.request, obj)

        return obj
    
···················check_object_permissions··········
    def check_object_permissions(self, request, obj):
        """
        Check if the request should be permitted for a given object.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_object_permission(request, self, obj):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
                
                
执行has_object_permission
    def has_object_permission(self, request, view, obj):
        return True

版本认证权限执行流程

1.首先所有方法都执行父类的apiview方法
2.执行dispatch方法

3.进行
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
4.
intiail request.user

如果代码中出现了CSRF使用装饰器即可

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator


def csrf_exempt(view_func):
    """
    Marks a view function as being exempt from the CSRF view protection.
    """
    # We could just do view_func.csrf_exempt = True, but decorators
    # are nicer if they don't have side-effects, so we return a new
    # function.
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

drf访问频率的限制

- 匿名用户,用IP作为用户唯一标记,但如果用户换代理IP,无法做到真正的限制。
- 登录用户,用用户名或用户ID做标识。可以做到真正的限制

知识点

{
    throttle_anon_1.1.1.1:[100121340,],
    1.1.1.2:[100121251,100120450,]
}


限制:60s能访问3次
来访问时:
    1.获取当前时间 100121280
    2.100121280-60 = 100121220,小于100121220所有记录删除全部剔除
    3.判断1分钟以内已经访问多少次了? 4 
        判断访问次数
    4.无法访问
停一会
来访问时:
    1.获取当前时间 100121340
    2.100121340-60 = 100121280,小于100121280所有记录删除
    3.判断1分钟以内已经访问多少次了? 0
    4.可以访问

执行流程

在视图类中配置 throttle_classes= [] 这是一个列表
实现 allow_request 方法 wait 是一个提示方法
返回 True/False。
True 可以继续访问,
False 表示限制
全局配置 
DEFAULT_THROTTLE_CLASSE,节流限制类
DEFAULT_THROTTLE_RATES 表示频率限制,比如 10/m 表示 每分钟 10 次
源码在 initial 中实现 check_throttles 方法


在进行节流之前已经做了版本认证权限
#dispatch分发    
    def initial(self, request, *args, **kwargs):
        #版本
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #认证
        self.perform_authentication(request)
        #权限
        self.check_permissions(request)
        #频率限制
        self.check_throttles(request)
1.先找allow_request本类没有去父类找 同dispatch
2.执行对应的rate获取对应scope 节流的频率
2.key是获取ip值 
3.history是获取ip地址 获取对应的访问时间记录
'''
self.history = self.cache.get(self.key, [])
'''
4.对应的访问记录等于ture获取到值,
把当前的时间减去一个自己设定的时间戳
'''
while self.history and self.history[-1] <= self.now - self.duration:
#条件满足 把最后一个值删除
#再去循环判断
            self.history.pop()
'''
5.然后判断访问次数有没有大于自己设定的值
'''
    if len(self.history) >= self.num_requests:
            满足条件走这
            return self.throttle_failure()
    不满足条件走这
    return self.throttle_success()
'''
 6.成功   def throttle_success(self):
        """
        Inserts the current request's timestamp along with the key
        into the cache.
        """
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True
    6.失败
        def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False#不可以访问
    7如果返回false进入等待时间
        def wait(self):
        """
        Returns the recommended next request time in seconds.
        """
        if self.history:
            remaining_duration = self.duration - (self.now - self.history[-1])
        else:
            remaining_duration = self.duration

        available_requests = self.num_requests - len(self.history) + 1
        if available_requests <= 0:
            return None

        return remaining_duration / float(available_requests)

源码

#dispatch分发    
    def initial(self, request, *args, **kwargs):
        #版本
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #认证
        self.perform_authentication(request)
        #权限
        self.check_permissions(request)
        #频率限制
        self.check_throttles(request)

    def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles():
            #找到当前类的allow_request
            if not throttle.allow_request(request, self):
                throttle_durations.append(throttle.wait())

        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]

            duration = max(durations, default=None)
            self.throttled(request, duration)

#执行类的allow_request
    def allow_request(self, request, view):
        """
        Implement the check to see if the request should be throttled.

        On success calls `throttle_success`.
        On failure calls `throttle_failure`.
        """
        if self.rate is None:
            return True
        获取
        '''
            def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()
              self.num_requests, self.duration = self.parse_rate(self.rate)
            当前这个类有一个获取rate的方法
        '''
        
        #执行get_rate
        读取settings设置的配置文件
        '''
                def get_rate(self):
    #如果不设置会进行报错
        try:
        #设置了就进行键取值
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
        '''
#通过键去取值/进行分割获取值
        '''
            def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)
        '''
    #这步继续往下执行继承类的get_cache_key
        self.key = self.get_cache_key(request, view)
        
    #匿名
    #继承了class AnonRateThrottle(SimpleRateThrottle):
        '''
               
    scope = 'anon'
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
}
#返回字符串格式化
throttle_(匿名)anon_(ip地址的拼接)1.1.1.1:[100121340,],
        '''
 #根据用户进行判断 重写get_cache_key
'''

'''
        
        if self.key is None:
            return True
        
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
            #元组返回的值num_requests
        if len(self.history) >= self.num_requests:
            #不可以访问
            return self.throttle_failure()
        #可以访问
        return self.throttle_success()
    '''
     6.成功 def throttle_success(self):
     #成功把访问的当前时间插入history
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True
    6.失败
        def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False#不可以访问
    
    '''

默认配置文件写法settings

REST_FRAMEWORK = {    
    throttle_classes = [AnonRateThrottle,]
    "DEFAULT_THROTTLE_RATES":{"anon":"3/m"}
    #这样写的原因 源码
    #通过匿名        
    scope = 'anon'
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
}

#获取全局设置的步骤
 def __init__(self):
        if not getattr(self, 'rate', None):
            #第一步
      
            self.rate = self.get_rate()
        '''
        以设置的键进行取值获取时间
    def get_rate(self):
如果不设置就进行报错
 try:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
        '''
            
        self.num_requests, self.duration = self.parse_rate(self.rate)
     #后面设置值格式
       #例 "DEFAULT_THROTTLE_RATES":{"anon":"3/m"}
    以/分割 前面是限制的次数 后面的是访问限制的时间
    
'''
 def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)

'''
之后执行对应字典的取值
key=throttle_(匿名)anon_(ip地址的拼接)1.1.1.1:
[100121340,],#值
self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()

根据匿名用户和id进行判断

全局配置

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'api.utils.throttles.throttles.LuffyAnonRateThrottle',
        'api.utils.throttles.throttles.LuffyUserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        #不重写的默认走这
        'anon': '10https://img.qb5200.com/download-x/day',   
        'user': '10https://img.qb5200.com/download-x/day',
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings

url写法

from django.conf.urls import url, include
from web.views.s3_throttling import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

settings写法局部

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_THROTTLE_RATES': {
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings.py

根据匿名ip或者user 进行判断

视图写法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework.throttling import SimpleRateThrottle


class LuffyAnonRateThrottle(SimpleRateThrottle):
    """
    匿名用户,根据IP进行限制
    """
    scope = "luffy_anon"

    def get_cache_key(self, request, view):
        # 用户已登录,则跳过 匿名频率限制
        if request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class LuffyUserRateThrottle(SimpleRateThrottle):
    """
    登录用户,根据用户token限制
    """
    #重写scope
    scope = "luffy_user"

    def get_ident(self, request):
        """
        认证成功时:request.user是用户对象;request.auth是token对象
        :param request: 
        :return: 
        """
        # return request.auth.token
        return "user_token"

    def get_cache_key(self, request, view):
        """
        获取缓存key
        :param request: 
        :param view: 
        :return: 
        """
        # 未登录用户,则跳过 Token限制
        if not request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class TestView(APIView):
    throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

    
    
    
    

源码

class AnonRateThrottle(SimpleRateThrottle):
scope = 'anon'
def get_cache_key(self, request, view):
if request.user.is_authenticated:
return None
# Only throttle unauthenticated requests.

    return self.cache_format % {
        'scope': self.scope,
        'ident': self.get_ident(request)
    }

def get_cache_key(self, request, view):
return self.get_ident(request)#获取对应的ip值

````

settings

# settings.py
    'DEFAULT_THROTTLE_RATES': {
      'Vistor': '3/m',
      'User': '10/m'
    },

drf总结

django'中可以免除csrftoken的认证

from django.views.decorators.csrf import csrf_exempt 
from django.shortcuts import HttpResponse
@csrf_exempt def index(request):    
    return HttpResponse('...')
# index = csrf_exempt(index)
urlpatterns = [    
    url(r'^index/$',index), 
              ]

drf中view进行csrftoken的认证

urlpatterns = [    url(r'^login/$',account.LoginView.as_view()), 
              ]
class APIView(View):    
    @classmethod    
    def as_view(cls, **initkwargs):        
        view = super().as_view(**initkwargs)     
        view.cls = cls        
        view.initkwargs = initkwargs
        # Note: session based authentication is explicitly CSRF validated,        
        # all other authentication is CSRF exempt.        
        return csrf_exempt(view)

1.写视图的方法

  • 第一种:原始APIView

    url(r'^login/$',account.LoginView.as_view()),
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework_jwt.settings import api_settings
    from rest_framework.throttling import AnonRateThrottle
    from api import models
    
    
    class LoginView(APIView):
        authentication_classes = []
        def post(self,request,*args,**kwargs):
            # 1.根据用户名和密码检测用户是否可以登录
            user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
            if not user:
                return Response({'code':10001,'error':'用户名或密码错误'})
    
            # 2. 根据user对象生成payload(中间值的数据)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            payload = jwt_payload_handler(user)
    
            # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接然后做hs256加密(加盐),再做base64加密。生成token
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            token = jwt_encode_handler(payload)
            return Response({'code': 10000, 'data': token})
  • 第二种:ListApiView等

    url(r'^article/$',article.ArticleView.as_view()),
    url(r'^article/(?P<pk>\d+)/$',article.ArticleDetailView.as_view()),
    from rest_framework.throttling import AnonRateThrottle
    from rest_framework.response import Response
    from rest_framework.generics import ListAPIView,RetrieveAPIView
    from api import models
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(ListAPIView):
        authentication_classes = []
        # throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = ArticleSerializer
    
    class ArticleDetailView(RetrieveAPIView):
        authentication_classes = []
        queryset = models.Article.objects.all()
        serializer_class = ArticleDetailSerializer
  • 第三种:

    url(r'^article/$',article.ArticleView.as_view({"get":'list','post':'create'})),
        url(r'^article/(?P<pk>\d+)/$',article.ArticleView.as_view({'get':'retrieve','put':'update','patch':'partial_update','delete':'destroy'}))
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(GenericViewSet,ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin):
        authentication_classes = []
        throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = None
    
        def get_serializer_class(self):
            pk = self.kwargs.get('pk')
            if pk:
                return ArticleDetailSerializer
            return ArticleSerializer
    

drf 相关知识点梳理

  1. 装饰器

    
    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    @outer
    def index(a1):
        pass
    
    index()
    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    def index(a1):
        pass
    
    index = outer(index)
    
    index()
  2. django中可以免除csrftoken认证

    from django.views.decorators.csrf import csrf_exempt
    from django.shortcuts import HttpResponse
    
    @csrf_exempt
    def index(request):
        return HttpResponse('...')
    
    # index = csrf_exempt(index)
    
    urlpatterns = [
        url(r'^index/$',index),
    ]
    urlpatterns = [
        url(r'^login/$',account.LoginView.as_view()),
    ]
    
    class APIView(View):
        @classmethod
        def as_view(cls, **initkwargs):
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
    
            # Note: session based authentication is explicitly CSRF validated,
            # all other authentication is CSRF exempt.
            return csrf_exempt(view)
  3. 面向对象中基于继承+异常处理来做的约束

    class BaseVersioning:
        def determine_version(self, request, *args, **kwargs):
            raise NotImplementedError("must be implemented")
    
    class URLPathVersioning(BaseVersioning):
     def determine_version(self, request, *args, **kwargs):
            version = kwargs.get(self.version_param, self.default_version)
            if version is None:
                version = self.default_version
    
            if not self.is_allowed_version(version):
                raise exceptions.NotFound(self.invalid_version_message)
            return version
  4. 面向对象封装

    class Foo(object):
     def __init__(self,name,age):
         self.name = name
         self.age = age 
    
    obj = Foo('汪洋',18)
    class APIView(View):
        def dispatch(self, request, *args, **kwargs):
    
            self.args = args
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
         ...
    
     def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(), # [MyAuthentication(),]
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
  5. 面向对象继承

    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        pass
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        serilizer_class = "汪洋"
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
    
        def get_seriliser_class(self):
            return "咩咩"
    
    view = UserInfoView()
    view.dispatch()
  6. 反射

    class View(object):
     def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
  7. 发送ajax请求

    
    $.ajax({
     url:'地址',
     type:'GET',
     data:{...},
     success:function(arg){
         console.log(arg);
     }
    })
  8. 浏览器具有 "同源策略的限制",导致 发送ajax请求 + 跨域 存在无法获取数据。

    • 简单请求,发送一次请求。
    • 复杂请求,先options请求做预检,然后再发送真正请求
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>常鑫的网站</h1>
        <p>
            <input type="button" value="点我" onclick="sendMsg()">
        </p>
        <p>
            <input type="button" value="点他" onclick="sendRemoteMsg()">
        </p>
    
    
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script>
            function sendMsg() {
                $.ajax({
                    url:'/msg/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
            }
            function sendRemoteMsg() {
                $.ajax({
                    url:'http://127.0.0.1:8002/json/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
    
            }
        </script>
    </body>
    </html>
    
  9. 如何解决ajax+跨域?

    CORS,跨站资源共享,本质:设置响应头。
  10. 常见的Http请求方法

    get
    post
    put
    patch
    delete
    options
  11. http请求中Content-type请起头

    情况一:
        content-type:x-www-form-urlencode
        name=alex&age=19&xx=10
    
        request.POST和request.body中均有值。
    
    情况二:
        content-type:application/json
        {"name":"ALex","Age":19}
    
        request.POST没值
        request.body有值。
  12. django中F查询

  13. django中获取空Queryset

    models.User.object.all().none()
  14. 基于django的fbv和cbv都能实现遵循restful规范的接口

    
    def user(request):
        if request.metho == 'GET':
            pass
    
    
    class UserView(View):
        def get()...
    
        def post...
  15. 基于django rest framework框架实现restful api的开发。

    - 免除csrf认证
    - 视图(APIView、ListAPIView、ListModelMinx)
    - 版本
    - 认证
    - 权限
    - 节流
    - 解析器
    - 筛选器
    - 分页
    - 序列化
    - 渲染器
  16. 简述drf中认证流程?

    1.用户发来请求优先执行dispatch方法
    2.内部会封装reqeustd1
  17. 简述drf中节流的实现原理以及过程?匿名用户/非匿名用户 如何实现频率限制?

  18. GenericAPIView视图类的作用?

    他提供了一些规则,例如:
    
    class GenericAPIView(APIView):
        serializer_class = None
        queryset = None
        lookup_field = 'pk'
    
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
        def get_queryset(self):
            return self.queryset
    
        def get_serializer_class(self):
            return self.serializer_class
    
        def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
    
        @property
        def paginator(self):
            if not hasattr(self, '_paginator'):
                if self.pagination_class is None:
                    self._paginator = None
                else:
                    self._paginator = self.pagination_class()
            return self._paginator
    
    他相当于提供了一些规则,建议子类中使用固定的方式获取数据,例如:
    class ArticleView(GenericAPIView):
        queryset = models.User.objects.all()
    
        def get(self,request,*args,**kwargs):
            query = self.get_queryset()
    
    我们可以自己继承GenericAPIView来实现具体操作,但是一般不会,因为更加麻烦。
    而GenericAPIView主要是提供给drf内部的 ListAPIView、Create....
    class ListModelMixin:
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
    
    class ListAPIView(mixins.ListModelMixin,GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    class MyView(ListAPIView):
        queryset = xxxx 
        ser...
    总结:GenericAPIView主要为drf内部帮助我们提供增删改查的类LIstAPIView、CreateAPIView、UpdateAPIView、提供了执行流程和功能,我们在使用drf内置类做CURD时,就可以通过自定义 静态字段(类变量)或重写方法(get_queryset、get_serializer_class)来进行更高级的定制。
  19. jwt以及其优势。

    jwt前后端分离 用于用户认证
    jwt的实现原理:
        -用户登陆成功,会给前端返回一个tokon值。
        此token值只在前端保存
        token值分为
        一段类型和算法信息
        第二段用户信息和超时时间
        第三段前两段数据拼接之后进行has256再次加密+base64url
  20. 序列化时many=True和many=False的区别?

  21. 应用DRF中的功能进行项目开发

    *****
        解析器:request.query_parmas/request.data
        视图
        序列化
        渲染器:Response
    
    ****
        request对象封装
        版本处理
        分页处理
    ***
        认证
        权限
        节流
    • 基于APIView实现呼啦圈
    • 继承ListAPIView+ GenericViewSet,ListModelMixin实现呼啦圈

跨域

  • 域相同,永远不会存在跨域。
    • crm,非前后端分离,没有跨域。
    • 路飞学城,前后端分离,没有跨域(之前有,现在没有)。
  • 域不同时,才会存在跨域。
    • l拉勾网,前后端分离,存在跨域(设置响应头解决跨域)
由于浏览器具有同源策略的限制
对ajax请求的限制
同源同端口
不同源就是跨域
写了/api 不跨域 api访问django
api.xx.com跨域了访问django

简单请求

发一次请求

设置响应头就可以解决
from django.shortcuts import render,HttpResponse

def json(request):
    response = HttpResponse("JSONasdfasdf")
    response['Access-Control-Allow-Origin'] = "*"
    return response

复杂请求

预检option

请求

html写法

写法

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
@csrf_exempt
def put_json(request):
    response = HttpResponse("JSON复杂请求")
    if request.method == 'OPTIONS':
        # 处理预检
        response['Access-Control-Allow-Origin'] = "*"
        response['Access-Control-Allow-Methods'] = "PUT"
        return response
    elif request.method == "PUT":
        return response

解决跨域:CORS

本质在数据返回值设置响应头

from django.shortcuts import render,HttpResponse

def json(request):
    response = HttpResponse("JSONasdfasdf")
    response['Access-Control-Allow-Origin'] = "*"
    return response

通过jsonp

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="button" onclick="Jsonp1();"  value='提交'/>
    </p>

    <p>
        <input type="button" onclick="Jsonp2();" value='提交'/>
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function Jsonp1(){
            var tag = document.createElement('script');
            tag.src = "http://c2.com:8000/test/";
            document.head.appendChild(tag);
            document.head.removeChild(tag);

        }

        function Jsonp2(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'JSONP',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>

总结

条件:
    1、请求方式:HEAD、GET、POST
    2、请求头信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 对应的值是以下三个中的任意一个
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain
 
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

总结

  1. 由于浏览器具有“同源策略”的限制,所以在浏览器上跨域发送Ajax请求时,会被浏览器阻止。
  2. 解决跨域
    • 不跨域
    • CORS(跨站资源共享,本质是设置响应头来解决)。
      • 简单请求:发送一次请求
      • 复杂请求:发送两次请求

部署 collectstaic 收集静态文件

jwt

用于在前后端分离时,实现用户登录相关。

1.知识点

1.2jwt代替token 进行优化

用户登录成功之后,生成一个随机字符串,给前端。
    - 生成随机字符串
        加密信息                            
        #{typ:"jwt","alg":'HS256'}   
        # 加密手段segments.append(base64url_encode(json_header)) 
        98qow39df0lj980945lkdjflo.
        #第二部分的信息 {id:1,username:'alx','exp':10}
        #加密手段segments.append(base64url_encode(payload))
        saueoja8979284sdfsdf.
        #两个密文拼接加盐
        asiuokjd978928374
        - 类型信息通过base64加密
        - 数据通过base64加密
        - 两个密文拼接在h256加密+加盐
    - 给前端返回token值只在前端
token是由。分割的三段组成
    - 第一段:类型和算法信息
    -   第二段。 用户的信息和超时时间
     - 第三段:hs256(前两段拼接)加密 + base64url 
         - 以后前端再次发来信息时       
            - 超时验证        
            - token合法性校验 

前端获取随机字符串之后,保留起来。
以后再来发送请求时,携带98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375。


后端接收到之后
    1.取出第二部分进行时间的判断
    2. 把前面两个进行加密对第三个值进行加密
    - token只在前端保存,后端只负责校验。    
    - 内部集成了超时时间,后端可以根据时间进行校验是否超时。       - 由于内部存在hash256加密,所以用户不可以修改token,只要一修改就认证失败。
    
    
    
    
一般在前后端分离时,用于做用户认证(登录)使用的技术。
jwt的实现原理:
    - 用户登录成功之后,会给前端返回一段token。
    - token是由.分割的三段组成。
        - 第一段:类型和算法信心
        - 第二段:用户信息+超时时间
        - 第三段:hs256(前两段拼接)加密 + base64url
    - 以后前端再次发来信息时
        - 超时验证
        - token合法性校验
优势:
    - token只在前端保存,后端只负责校验。
    - 内部集成了超时时间,后端可以根据时间进行校验是否超时。
    - 由于内部存在hash256加密,所以用户不可以修改token,只要一修改就认证失败。

1.2token

用户登陆成功之后,生成一个随机字符串,自己保留一份,给前端返回一份。
以后前端再来发请求时,需要携带字符串
后端对字符串进行校验

流程

1.安装

pip3 install djangorestframework-jwt

2.注册

INSTALLED_APPS = [    
    'django.contrib.admin',    
    'django.contrib.auth',    
    'django.contrib.contenttypes',    
    'django.contrib.sessions',   
    'django.contrib.messages',    
    'django.contrib.staticfiles',    
    'api.apps.ApiConfig',    
    'rest_framework',    
    'rest_framework_jwt']

3.源码剖析和实现流程

from rest_framework_jwt.views import obtain_jwt_token

具体流程

用户信息加密
jwt_payload_hander = api_settings.JWT_PAYLOAD_HANDLER
第三段信息的加密
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
#解密
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


def jwt_payload_handler(user):
    username_field = get_username_field()
    username = get_username(user)

    warnings.warn(
        'The following fields will be removed in the future: '
        '`email` and `user_id`. ',
        DeprecationWarning
    )

    payload = {
        'user_id': user.pk,
        'username': username,
        'exp': datetime.utcnow() +
        #默认5分钟
        api_settings.JWT_EXPIRATION_DELTA
        #如果有email会把email配置上
         if hasattr(user, 'email'):
        payload['email'] = user.email
    if isinstance(user.pk, uuid.UUID):
        #如果有user_id会把pk值配置上
        payload['user_id'] = str(user.pk)

    payload[username_field] = username
        
        
        #源码
       settings= 'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300)
 ,
       
    }

加密的具体实现流程

  
 api_settings.JWT_ENCODE_HANDLER -->
    
    加密的具体实现
    
    def jwt_encode_handler(payload):
        #payload传入
        key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload)
        #加盐
        #SECRET_KEY = '+gr4bbq8e$yqbd%n_h)2(osz=bmk1x2+o6+w5g@a4r1#3%q1n*'
        return jwt.encode(
            payload,#类型信息头信息
            key,#加盐
            #默认封装传入了hs256
            api_settings.JWT_ALGORITHM
            # 'JWT_ALGORITHM': 'HS256',
            ).decode('utf-8')
    encode默认继承父类的 
    父类的encode方法
    # Header
        header = {'typ': self.header_typ, 'alg': algorithm}
        #self.header_typ #-- 》   header_typ = 'JWT'
        signing_input = b'.'.join(segments)#把类型信息和数据用.的形式拼接到了一起
         try:
            alg_obj = self._algorithms[algorithm]#执行算法
            key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input, key)#把拼接起来的值进行二次加密 成为第三个信息
            '''
        except KeyError:
            if not has_crypto and algorithm in requires_cryptography:
                raise NotImplementedError(
                    "Algorithm '%s' could not be found. Do you have cryptography "
                    "installed?" % algorithm
                )
            else:
                raise NotImplementedError('Algorithm not supported')
        '''
        segments.append(base64url_encode(signature))#再把第三个信息放入列表 对第三个信息进行base64进行加密

        return b'.'.join(segments)#用.的形式再把第三个数据拼接起来 进行返回
        98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375
    
       

解密

将token分割成 header_segment、payload_segment、crypto_segment 三部分
对第一部分header_segment进行base64url解密,得到header
对第二部分payload_segment进行base64url解密,得到payload
对第三部分crypto_segment进行base64url解密,得到signature
对第三部分signature部分数据进行合法性校验
拼接前两段密文,即:signing_input
从第一段明文中获取加密算法,默认:HS256
使用 算法+盐 对signing_input 进行加密,将得到的结果和signature密文进行比较。

jwt的原理和优势

一般在前后端分离时,用于做用户认证(登录)使用的技术。
jwt的实现原理:
    - 用户登录成功之后,会给前端返回一段token。
    - token是由.分割的三段组成。
        - 第一段:类型和算法信心
        - 第二段:用户信息+超时时间
        - 第三段:hs256(前两段拼接)加密 + base64url
    - 以后前端再次发来信息时
        - 超时验证
        - token合法性校验
#优势:
    - token只在前端保存,后端只负责校验。
    - 内部集成了超时时间,后端可以根据时间进行校验是否超时。
    - 由于内部存在hash256加密,所以用户不可以修改token,只要一修改就认证失败。

具体使用

用户登陆

import uuid
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning
from rest_framework import status

from api import models

class LoginView(APIView):
    """
    登录接口
    """
    def post(self,request,*args,**kwargs):

        # 基于jwt的认证
        # 1.去数据库获取用户信息
        from rest_framework_jwt.settings import api_settings
        #头信息
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        #第三个数据的加密
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        
        user = models.UserInfo.objects.filter(**request.data).first()
        if not user:
            return Response({'code':1000,'error':'用户名或密码错误'})
    
        '''
        'user_id': user.pk,
        'username': username,
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA
        '''
            #头信息的处理 
        payload = jwt_payload_handler(user)
        #对三段信息的编码 加密
        token = jwt_encode_handler(payload)
        return Response({'code':1001,'data':token})
    
    
    
    
#第二种方式
class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        # 1.根据用户名和密码检测用户是否可以登录
        user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
        if not user:
            return Response({'code':10001,'error':'用户名或密码错误'})

        # 2. 根据user对象生成payload(中间值的数据)
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        payload = jwt_payload_handler(user)

        # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接然后做hs256加密(加盐),再做base64加密。生成token
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        token = jwt_encode_handler(payload)
        return Response({'code': 10000, 'data': token})

用户认证

开始解码

from rest_framework.views import APIView
from rest_framework.response import Response

# from rest_framework.throttling import AnonRateThrottle,BaseThrottle


class ArticleView(APIView):
    # throttle_classes = [AnonRateThrottle,]

    def get(self,request,*args,**kwargs):
        # 获取用户提交的token,进行一步一步校验
        import jwt
        from rest_framework import exceptions
        from rest_framework_jwt.settings import api_settings
        #进行解码
        jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
        #获取到加密之后的字符串
        jwt_value = request.query_params.get('token')
        try:
            #对token进行解密
            payload = jwt_decode_handler(jwt_value)
            #判断签名是否过期
        except jwt.ExpiredSignature:
            msg = '签名已过期'
            raise exceptions.AuthenticationFailed(msg)
            #判断是否被篡改
        except jwt.DecodeError:
            msg = '认证失败'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        print(payload)

        return Response('文章列表')

settings设置

'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300)
    #默认五分钟有效
#自定义
import datetime
JWT_AUTH = {
    "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10)
}

视图的mixins写法

ListModelMixin

执行list方法

paramiko

用于帮助开发者通过代码远程连接服务器

公钥和私钥进行连接服务器 比较方便

$ssh -copy-id -i .ssh /id ras.pub

基于用户名密码连接:

需要一直去使用输入

import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()

# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接服务器
ssh.connect(hostname='192.168.159.128', port=22, username='root', password='ziwen123')

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()


发送数据
import requests

requests.post(
url = "http://127.0.0.1:8000/api/v1/server/",
json={'server':data,'host':'192.16.12.66'}
)
```

远程执行命令【公钥和私钥】(公钥必须提前上传到服务器)

import paramiko
 
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
 
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='c1.salt.com', port=22, username='wupeiqi', key=private_key)
 
# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
 
# 关闭连接
ssh.close()

远程上传和下载文件【用户名和密码】

import paramiko
 
transport = paramiko.Transport(('hostname',22))
transport.connect(username='wupeiqi',password='123')
 
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
 
transport.close()

远程上传和下载文件【公钥和私钥】

import paramiko
 
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
 
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', pkey=private_key )
 
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
 
transport.close()

补充:通过私钥字符串也可以连接远程服务器

key = """-----BEGIN RSA PRIVATE KEY----MIIG5AIBAAKCAYEAu0fkMInsVRnIBSiZcVYhKuccWCh6hapYgB1eSOWZLz3+xFGy G5p2z8HgiHzfT838gAm+5OajuyAuE4+fHI77LXSg+pLbr1FhPVKAP+nbsnLgvHty ykZmt74CKKvZ08wdM7eUWJbkdpRNWmkwHBi99LeO0zYbHdXQ+m0P9EiWfdacJdAV RDVCghQo1/IpfSUECpfQK1Hc0126vI8nhtrvT3V9qF420U1fwW9GJrODl71WRqvJ BgSsKsjV16f0RKARESNmtA2vEdvMeutttZoO4FbvZ+iLKpcRM4LGm2+odryr8ijv dCPCLVvoDExOPuqP1dgt5MWcCWf6ZNhMwAs/yvRHAKetvo5gtz8YvzwlikopCLM7 bS6C6woyppMHfIPjoGJ6JuKpeaWtAgugOw/oVvj1rRYoCv48R13NftqhkFD1KD8z km9CjDC8hv+2DmIedtjvVwUz2QF4PN/RC/i1jo3+3rbP1DLu9emTHiortBBrpQ5o K+y4Rzv+6NusD6DHAgMBAAECggGBAJ4hTaNOUaZpZmI0rZrsxoSbL2ugghNqid9i 7MFQW89v4TWSZXi5K6iwYw3bohKYMqNJl01fENBnk4AgvJA4ig0PdP0eEzAs3pYQ mwlcRIygQvHiqkHwv7pVTS1aLUqQBfgtAazre2xEPCwitOSEX5/JfWcJQEwoxZMt k1MIF0mZc67Zy5sT/Vwn+XScnDt2jbsEBFkPfg1aDto3ZYCQS5Aj/D21j0OauUdy 1SDIYkw1Kivx0IKsX1Kg0S6OOcnX/B6YrJvisrlQDeZnWlTsTyKSVTekIybJjUHE ZgLIIbifSbTW1Bv1iCkDAJBd4Cj4txjXPIgea9ylZ39wSDSV5Pxu0t/M3YbdA26j quVFCKqskNOC+cdYrdtVSij2Ypwov67HYsXC/w32oKO7tiRqy51LAs/WXMwQeS5a 8oWDZLiYIntY4TCYTVOvFlLRtXb+1SbwWKjJdjKvdChv4eo/Ov5JEXD2FVbVC/5E Qo3jyjIrt1lrwXUdpJa0/iz4UV33wQKBwQDprCPZVCI7yK/BWTmUvCcupotNk6CC +QIKDcvVxz63YFD5nXto4uG7ywXR6pEwOwmycO0CBuouvlPdSioQ3RYi6k0EO3Ch 9dybC5RZ3MENBHROHvU3mp01EWPUYnXAwNpvknujJqfXMxyURZvvox7hOnu/s3m4 C3eCBrMMg+uqNZDbLqAymw3pMGhHVWjy5oO8eLuLeJv6er+XoSSPNb21Da7StdQS fBPQ1H0/+RXnhFJOzANc4mRZcXMCNGVZX6MCgcEAzSz3evuCRQ47AaSOrDd89jAw PgpT+PG4gWw1jFZqHTbQ8MUl3YnElOVoaWRdIdDeslg9THg1cs5Yc9RrbIibyQjV F9k/DlXGo0F//Mgtmr7JkLP3syRl+EedRbu2Gk67XDrV7XIvhdlsEuSnEK9xOiB6 ngewM0e4TccqlLsb6u7RNMU9IjMu/iMcBXKsZ9Cr/DENmGQlTaRVt7G6UcAYGNgQ toMoCQWjR/HihlZHssLBj9U8uPyD38HKGy2OoXyNAoHBAKQzv9lHYusJ4l+G+IyJ DyucAsXX2HJQ0tsHyNYHtg2cVCqkPIV+8UtKpmNVZwMyaWUIL7Q98bA5NKuLIzZI dfbBGK/BqStWntgg8fWXx90C5UvEO2MAdjpFZxZmvgJeQuEmWVVTo5v4obubkrF5 ughhVXZng0AOZsNrO8Suqxsnmww6nn4RMVxNFOoTnbUawTXezUN71HfWa+38Ybl0 9UNWQyR0e3slz7LurrkWqwrOlBwlBrPtrsCflUbWVOXR6wKBwDFq+Dy14V2SnOG7 aeXPA5kkaCo5QJqAVglOL+OaWLqqnk6vnXwrl56pVqmz0762WT0phbIqbe02CBX1 /t3IVYVpTDIPUGG6hTqDJzmSWXGhLFlfD3Ulei3/ycCnAqh5eCUxwp8LVqjtgltW mWqqZyIx+nafsW/YgWqyYu4p1wKR/O+x5hSbsWDiwfgJ876ZgyMeCYE/9cAqqb6x 3webtfId8ICVPIpXwkks2Hu0wlYrFIX5PUPtBjJZsb00DtuUbQKBwF5BfytRZ0Z/ 6ktTfHj1OJ93hJNF9iRGpRfbHNylriVRb+hjXR3LBk8tyMAqR4rZDzfBNfPip5en 4TBMg8UATf43dVm7nv4PM2e24CRCWXMXYl7G3lFsQF/g7JNUoyr6bZQBf3pQcBw4
IJ38IcKV+L475tP4rfDrqyJz7mcJ+90a+ai5cSr9XoZqviAqNdhvBq5LjGOLkcdN bS0NAVVoGqjqIY/tOd2NMTEF6kVoYfJ7ZJtjxk/R3sdbdtajV3YsAg== -----END RSA PRIVATE KEY-----"""

import paramiko from io import StringIO
private_key = paramiko.RSAKey(file_obj=StringIO(key))
# 创建SSH对象 ssh = paramiko.SSHClient() 
# 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
# 连接服务器 
ssh.connect(hostname='192.168.16.85', port=22, username='root', pkey=private_key)
# 执行命令 
stdin, stdout, stderr = ssh.exec_command('df') # 获取命令结果 
result = stdout.read()
# 关闭连接 
ssh.close()
print(result)

生成

公司员工基于xshell连接服务器 
用户名和密码 公钥和私钥(rsa) 

生成公钥和私钥

ssh-keygen.exe -m pem
在当前用户家目录会生成: 
.ssh/id_rsa.pub    .ssh/id_rsa

把公钥放到服务器

ssh-copy-id -i ~.ssh/id_rsa.pub root@192.168.16.85

以后再连接服务器时,不需要在输入密码

ssh root@192.168.16.85

总结

···

什么是前后端分离

固定知识返回json数据

drf组件

帮助我们在django框架基础上快速搭建遵循restful规范接口的程序

drf组件的功能

解析器,解析请求体中的数据,将其变成我们想要的格式.request.data

序列化 对对象或者对象列表(queryset)进行序列化 操作以及表单验证的功能

视图 继承APIView(在内部apiview继承了django的view)

postman

模拟浏览器进行发送请求

查找模板的顺序

优先根目录下的templates
retframework 有一个templates
根据app的注册顺序去每个app的templates目录中找

个人补充

   import uuid
   str(uuid.uuid4())#随机字符串
models.表名.objects.all().none() 返回是一个none

添加数据库
    models.Disk.objects.create(**之后的字典,外键id=外键id)
    
settings里面的基本都是用import importlib
import importlib 
利用字符串直接获得模块

登陆字段

models.py 
input的blank=TRUE验证可以为空 null=True数据库可以为空 ,unique,唯一
charfiled choice=sex_type(等于一个元组)  id是值 名字是键 ((键,值),(键,值))  max_length=16
verbose_name=’QQ昵称‘ 和label一样 显示作用于admin可以不写
models.forekey('self')自关联
multiselectField('咨询')需要第三方模块下载 多选下拉框
verbose_name='客户信息表' admin创建表的时候的表名显示的数据
unique_together=联合唯一 下载第三方模块 djangoMultiSelectField   course = MultiSelectField("咨询课程", choices=course_choices) #多选,并且存成一个 列表的格式,通过modelform来用的时候,会成为一个多选框

水平分表

把数据比较多的平行分成两个表(分出来一个详细表)

mysqldb

mysqldb是一个接口连接到mysql数据库服务器

request是什么

 #请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了 HttpRequest 对 象, 
    请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了HttpRequest 对象 
    1.该对象由 HttpRequest 祖类创建。     
    这个是wsgi是从属于类 
    2.每一个请求都会生成一个 HttpRequest 对象, 
    3.django会将这个对象自动传递给响应的视图函数, 
    4.视图函数第一个参数传给视图函数。这个参数就是django视图函数的第一个参数,通常写成request。 
    5.一般视图函数约定俗成地使用 request 参数承接这个对象

表删除

book_obj= 第一种 book_obj.objects.remove(1) 第二种 clear 清空 第三种 set(['1','5'])  
# 先清除再添加,相当于修改 
#把原来的删除重新添加进去,注意不是原位置

Form所有内置字段

Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型

ajax补充

traditional:true, 

在ajax设置该属性就可以往后端传递数组

拓展性知识

ajax的traditional属性

ajax的traditional属性 

jquery框架的ajax参数除了常用的

$.ajax({
  url: 'xxx',
  type: 'xxx',
  data: 'xxx',
  success: 'xxx'
  ...
})

另外还有一个参数需要特别注意下traditional默认值是false。
ajax做数据处理时,是通过jQuery.param( obj, traditional )该方法进行处理。

jquery1.4版本以后
traditional参数,默认false的时候如果是{a:{b:'value'}}是处理成a[b],这样形式,如果是数组:data:{a:[1,2]},是解析成a[]=1&a[]=2,这种方式后台确实要做兼容(取a[b]或a[])来取值。
在数组情况下把traditional参数设置成true,是解析成a=1&a=2,对后台直接通过a拿数据。但是实验了下object情况,把traditional设置成true,转成了a=[object+Object],这样就是费的了。false时解析成上面的形式应该就是类型指示作用,我看到这种格式就知道请求数据是Array还是object了,true就是按照form提交的方式传值。

当需要把多个checkbox的value值通过ajax传到servlet时就需要加上traditional参数并且为true,如下代码:

//批量删除
            $("#alldel").click(function () {
                var ids = $(".che:checked");
                var items = new Array();
                for (var i=0;i<ids.size();i++){
                    items.push(ids[i].value);
                }
                if (confirm("您确定要删除选中数据吗?")) {
                    $.ajax({
                        type: "post",
                        url: "UserServlet?action=deleteAll",
                        data:{items:items},
               //防止深度序列化
                        traditional :true,
                        async: true,
                        success: function(data) {
                            var da = JSON.parse(data);
                            alert(da.msg)
                            app.count();
                        },
                        error: function(data) {
                            console.info("error: " + data.responseText);
                        }
                    });
                }
            });
    
    
   
 

在后台我们就可以用 String[] items = request.getParameterValues("items")  进行接收前端传过来的数组,如下图:

request.POST.get() #获取值

这样就可以用SQL语句 delete from xxx where id in(x,x)  多参数的形式进行操作了。

表结构很少有一对一之间的关系

很多时候都是1对多 多对多之间的关系

input 消除自动记忆功能

在html里就可以直接清除了<input type="text" autocomplete="off"> input 的autocomplete属性默认是on:其含义代表是否让浏览器自动记录之前输入的值 off:则关闭记录

查询多个用反向查询set

# 多对多
如果post请求多个值使用getlist(字段)
写法
obj=request.POST.getlist("author_id")
#取得的是一个列表
    # 海狗的怂逼人生 是哪些作者写的 -- 正向查询
     obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
     ret = obj.authors.all()#可以直接查询到作者对应的名字 (直接查询到)
    
     print(ret)  #<QuerySet [<Author: 王洋>, <Author: 海狗>]>
     for i in ret:
         print(i.name)

    # 查询一下海狗写了哪些书 -- 反向查询
     obj = models.Author.objects.filter(name='海狗').first()
     ret = obj.book_set.all()
     print(ret)
     for i in ret:
         print(i.publishs.name)
         print(i.title)
     return HttpResponse('ok')

1.filter双下方法

1.2用i不区分大小写

2.下拉框(select)选择出来的就是列表

如果post请求多个值使用getlist(字段) 写法 obj=request.POST.getlist("author_id") #取得的是一个列表

request.Get.get()取到的值是 ?后面对应

此写法的好处不需要添加多余的路径 不用分组路径 127.0.0.1/home?id=3 request.post.get()取到值是3

mysqldb是什么

mysqldb是一个接口连接到mysql数据库服务器从python

wsgiref

from wsgiref.simple_server import make_server 
# wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的 socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便) #函数名字随便起 
def application(environ, start_response):   
    '''    
    :param environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多 你想要拿到的信息    
    :param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数    
    :return:    
    '''    
    start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')])    
    print(environ)    
    print(environ['PATH_INFO'])  
    #输入地址127.0.0.1:8000,这个打印的是'/',输入的是 127.0.0.1:8000/index,打印结果是'/index' 
    return [b'<h1>Hello, web!</h1>']
#和咱们学的socketserver那个模块很像啊 
httpd = make_server('127.0.0.1', 8080, application)
print('Serving HTTP on port 8080...') 
# 开始监听HTTP请求: httpd.serve_forever()#send application的返回值 一个列表
 

requset是什么

 #请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了 HttpRequest 对 象, 
请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了HttpRequest 对象 
1.该对象由 HttpRequest 祖类创建。 
    这个是wsgi是从属于类
2.每一个请求都会生成一个 HttpRequest 对象,
3.django会将这个对象自动传递给响应的视图函数, 
4.视图函数第一个参数传给视图函数。这个参数就是django视图函数的第一个参数,通常写成request。 
5.一般视图函数约定俗成地使用 request 参数承接这个对象

示例

print(requset) <WSGIRequest: GET '/app01/library/'>

表删除

book_obj= 第一种 book_obj.objects.remove(1) 
第二种 clear 清空 
第三种 set(['1','5'])  # 先清除再添加,相当于修改 #把原来的删除重新添加进去,注意不是原位置

知识点补充

bootstrapSweetAlert插件

$(".btn-danger").on("click", function() { 
    swal({  
        title: "你确定要删除吗?",
          text: "删除可就找不回来了哦!",
        #标签显示的   type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-danger",
          confirmButtonText: "删除",
          cancelButtonText: "取消",
          closeOnConfirm: false 
    },   function() {    #
        注意当前的this是指谁调用了这个   
        var deleteId = $(this).parent().parent().attr("data_id");   
        $.ajax({    
            url: "https://img.qb5200.com/download-x/delete_book/",
                type: "post",
                data: {
                "id": deleteId
            },
                success: function(data) {     
                if (data.status === 1) {      
                    swal("删除成功!", "你可以准备跑路了!", "success");    
                } else {      
                    swal("删除失败", "你可以再尝试一下!", "error")    
                }   
            }  
        }) 
    });
})

装饰器的补充

falask路由会用 装饰器的原理 开放封闭原则 在不改变原函数代码的前提下, 在函数前函数后增加新的功能 手写简单装饰器 
def wrapper(f):   
    def inner( * args, ** kwargs):     
        return f( * args, ** kwargs)   
    return inner 
补充:
import functools 
def wrapper(f):  
@functools.wraps(f)
# 加了之后保留函数元数据( 名字和注释)   
def inner(*args,**kwargs):     
    return f(*args,**kwargs)   
        return inner 
        ''' 
        1.执行wrapper 
        2.返回inner重新赋值index 
        '''
index=wrapper(index) 

@wrapper 
def index(a1, a2):
    return a1+a2 
print(index.__name__) #查看函数名   
print(index.__doc__) #查看注释 
1.不加内部装饰器functools.wraps index执行的是inner 2.加了保留函数元数据(名字和注释)

orm补充

verbose_name#admin显示的名字
以后在model form 和form中使用
写法
    name=models.CharField(verbose_name="出版社名称",max_length=32)

路由系统中记得加入终止符$

这种写法的好处: 防止/index/下的子页面访问路径被之前路径的匹配
在urls.py路由中写法 urlpatterns = [  url(r'^admin/', admin.site.urls),  url(r'^index/$',views.index) 
 ]

模板查找顺序

1.先从根目录去找templates
2.在根据app注册顺序去每个app的templates中找(从上往下)

加载全部内容

相关教程
猜你喜欢
用户评论