zoukankan      html  css  js  c++  java
  • django-rest-framework

    web开发模式

      前后端混合开发(前后端不分离):返回的是html的内容,需要写模板
      前后端分离:只专注于写后端接口,返回json,xml格式数据

    api接口 

      通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介

    Restful规范

      RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。

    # 10条规范
    1  数据的安全保障:url链接一般都采用https协议进行传输 注:采用https协议,可以提高数据交互过程中的安全性
    2 接口特征表现,一看就知道是个api接口
        - 用api关键字标识接口url:
          - [https://api.baidu.com](https://api.baidu.com/)
          - https://www.baidu.com/api
          注:看到api字眼,就代表该请求url链接是完成前后台数据交互的
          -路飞的接口:https://api.luffycity.com/api/v1/course/free/
    3 多数据版本共存
        - 在url链接中标识数据版本
        - https://api.baidu.com/v1
        - https://api.baidu.com/v2
        注:url链接中的v1、v2就是不同数据版本的体现(只有在一种数据资源有多版本情况下)
    4 数据即是资源,均使用名词(可复数)
        - 接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
          - https://api.baidu.com/users
          - https://api.baidu.com/books
          - https://api.baidu.com/book
    
          注:一般提倡用资源的复数形式,在url链接中奖励不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user
        - 特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
    
          - https://api.baidu.com/place/search
          - https://api.baidu.com/login
    5 资源操作由请求方式决定(method)
        - 操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作
          - https://api.baidu.com/books - get请求:获取所有书
          - https://api.baidu.com/books/1 - get请求:获取主键为1的书
          - https://api.baidu.com/books - post请求:新增一本书书
          - https://api.baidu.com/books/1 - put请求:整体修改主键为1的书
          - https://api.baidu.com/books/1 - patch请求:局部修改主键为1的书
          - https://api.baidu.com/books/1 - delete请求:删除主键为1的书
    6 过滤,通过在url上传参的形式传递搜索条件
        - https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
        - https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
        - https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        - https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        - https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
            
    7 响应状态码
       7.1 正常响应
        - 响应状态码2xx
          - 200:常规请求
          - 201:创建成功
       7.2 重定向响应
        - 响应状态码3xx
          - 301:永久重定向
          - 302:暂时重定向
       7.3 客户端异常
        - 响应状态码4xx
          - 403:请求无权限
          - 404:请求路径不存在
          - 405:请求方法不存在
        7.4 服务器异常
        - 响应状态码5xx
          - 500:服务器异常
    8 错误处理,应返回错误信息,error当做key { error: "无权限操作" } 9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范 GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档 10 需要url请求的资源需要访问资源的请求链接 # Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么 { "status": 0, "msg": "ok", "results":[ { "name":"肯德基(罗餐厅)", "img": "https://image.baidu.com/kfc/001.png" } ... ] }

    drf的安装和简单使用

    # 安装:pip install djangorestframework==3.10.3
    # 使用
        1 在setting.py 的app中注册
            INSTALLED_APPS = [
            'rest_framework'
            ]
        2 在models.py中写表模型
            class Book(models.Model):
                nid=models.AutoField(primary_key=True)
                name=models.CharField(max_length=32)
                price=models.DecimalField(max_digits=5,decimal_places=2)
                author=models.CharField(max_length=32)
        3 新建一个序列化类
            from rest_framework.serializers import ModelSerializer
            from app01.models import  Book
            class BookModelSerializer(ModelSerializer):
                class Meta:
                    model = Book
                    fields = "__all__"
        4 在视图中写视图类
            from rest_framework.viewsets import ModelViewSet
            from .models import Book
            from .ser import BookModelSerializer
            class BooksViewSet(ModelViewSet):
                queryset = Book.objects.all()
                serializer_class = BookModelSerializer
        5 写路由关系
            from app01 import views
            from rest_framework.routers import DefaultRouter
            router = DefaultRouter()  # 可以处理视图的路由器
            router.register('book', views.BooksViewSet)  # 向路由器中注册视图集
              # 将路由器中的所以路由信息追到到django的路由列表中
            urlpatterns = [
                path('admin/', admin.site.urls),
            ]
            #这是什么意思?两个列表相加
            # router.urls  列表
            urlpatterns += router.urls
            
        6 启动,在postman中测试即可

    cbv源码

    # ModelViewSet继承View(django原生View)
    # APIView继承了View
    
    # 先读View的源码
    from django.views import View
    
    # urls.py
    path('books1/', views.Books.as_view()),  #在这个地方应该写个函数内存地址,views.Books.as_view()执行完,是个函数内存地址,as_view是一个类方法,类直接来调用,会把类自动传入
    放了一个view的内存地址(View--》as_view--》内层函数)
    
    # 请求来了,如果路径匹配,会执行,  函数内存地址(request)
    def view(request, *args, **kwargs):
        #request是当次请求的request
        self = cls(**initkwargs)  #实例化得到一个对象,Book对象
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
    
     
    def dispatch(self, request, *args, **kwargs):
            #request是当次请求的request   self是book对象
            if request.method.lower() in self.http_method_names:
                #handler现在是:
                handler=getattr(self,'get'),你写的Book类的get方法的内存地址
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)  #执行get(request)

    APIView源码分析

    #from rest_framework.views import APIView
    # urls.py
    path('booksapiview/', views.BooksAPIView.as_view()),  #在这个地方应该写个函数内存地址
    
    #APIView的as_view方法(类的绑定方法)
       def as_view(cls, **initkwargs):
            view = super().as_view(**initkwargs)  # 调用父类(View)的as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
            # 以后所有的请求,都没有csrf认证了,只要继承了APIView,就没有csrf的认证
            return csrf_exempt(view)
     
    
    #请求来了---》路由匹配上---》view(request)---》调用了self.dispatch(),会执行apiview的dispatch
        
    # APIView的dispatch方法
        def dispatch(self, request, *args, **kwargs):
    
            self.args = args
            self.kwargs = kwargs
            # 重新包装成一个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)
    
                # 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
       
    # APIView的initial方法
         def initial(self, request, *args, **kwargs):
            # 认证组件:校验用户 - 游客、合法用户、非法用户
            # 游客:代表校验通过,直接进入下一步校验(权限校验)
            # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
            # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
            self.perform_authentication(request)
            # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
            # 认证通过:可以进入下一步校验(频率认证)
            # 认证失败:抛出异常,返回403权限异常结果
            self.check_permissions(request)
            # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
            # 没有达到限次:正常访问接口
            # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
            self.check_throttles(request)
    from rest_framework.request import Request
    # 只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了
    # 老的request在新的request._request
    # 以后使用reqeust对象,就像使用之前的request是一模一样(因为重写了__getattr__方法)
      def __getattr__(self, attr):
            try:
                return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
            except AttributeError:
                return self.__getattribute__(attr)
    
     # request.data 感觉是个数据属性,其实是个方法,@property,修饰了
        它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
     #get请求传过来数据,从哪取?
        request.GET
        @property
        def query_params(self):
            """
            More semantically correct name for request.GET.
            """
            return self._request.GET
        
        #视图类中
         print(request.query_params)  #get请求,地址中的参数
         # 原来在
         print(request.GET)

    局部禁用csrf

    # 在视图函数上加装饰器@csrf_exempt
    # csrf_exempt(view)这么写和在视图函数上加装饰器是一毛一样的
    
    #urls.py中看到这种写法
    path('tset/', csrf_exempt(views.test))

    序列化组件

      1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
      2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
      3. 反序列化,完成数据校验功能

    使用

    # ser.py
    class BookSerializer(serializers.Serializer):
        # id=serializers.CharField()
        name=serializers.CharField()
        # price=serializers.DecimalField()
        price=serializers.CharField()
        author=serializers.CharField()  
        publish=serializers.CharField()
        
    # views.py
    class BookView(APIView):
        def get(self,request,pk):
            book=Book.objects.filter(id=pk).first()
            #用一个类,毫无疑问,一定要实例化
            #要序列化谁,就把谁传过来
            book_ser=BookSerializer(book)  # 调用类的__init__
            # book_ser.data   序列化对象.data就是序列化后的字典
            return Response(book_ser.data)
        
    # urls.py
    re_path('books/(?P<pk>d+)', views.BookView.as_view())

    序列化组件修改数据

    1 写一个序列化的类,继承Serializer
    2 在类中写要反序列化的字段,想反序列化哪个字段,就在类中写哪个字段,字段的属性(max_lenth......)
        max_length    最大长度
        min_lenght    最小长度
        allow_blank    是否允许为空
        trim_whitespace    是否截断空白字符
        max_value    最小值
        min_value    最大值
    3 在视图类中使用,导入--》实例化得到序列化类的对象,把要要修改的对象传入,修改的数据传入
        boo_ser=BookSerializer(book,request.data)
        boo_ser=BookSerializer(instance=book,data=request.data)
    4 数据校验 if boo_ser.is_valid()
    5 如果校验通过,就保存
        boo_ser.save()  # 注意不是book.save()
    6 如果不通过,逻辑自己写
    
    
    7 如果字段的校验规则不够,可以写钩子函数(局部和全局)
            # 局部钩子
            def validate_price(self, data):   # validate_字段名  接收一个参数
                #如果价格小于10,就校验不通过
                # print(type(data))
                # print(data)
                if float(data)>10:
                    return data
                else:
                    #校验失败,抛异常
                    raise ValidationError('价格太低')
             # 全局钩子
                def validate(self, validate_data):   # 全局钩子
                    print(validate_data)
                    author=validate_data.get('author')
                    publish=validate_data.get('publish')
                    if author == publish:
                        raise ValidationError('作者名字跟出版社一样')
                    else:
                        return validate_data
    8 可以使用字段的author=serializers.CharField(validators=[check_author]) ,来校验
        -写一个函数
            def check_author(data):
                if data.startswith('sb'):
                    raise ValidationError('作者名字不能以sb开头')
                else:
                    return data
         -配置:validators=[check_author]
    # models.py
    class Book(models.Model):
        id=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
        author=models.CharField(max_length=32)
        publish=models.CharField(max_length=32)
    
    # ser.py
    
    # from rest_framework.serializers import Serializer  # 就是一个类
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    # 需要继承 Serializer
    
    
    def check_author(data):
        if data.startswith('sb'):
            raise ValidationError('作者名字不能以sb开头')
        else:
            return data
    
    
    class BookSerializer(serializers.Serializer):
        # id=serializers.CharField()
        name=serializers.CharField(max_length=16,min_length=4)
        # price=serializers.DecimalField()
        price=serializers.CharField()
        author=serializers.CharField(validators=[check_author])  # validators=[] 列表中写函数内存地址
        publish=serializers.CharField()
    
        def validate_price(self, data):   # validate_字段名  接收一个参数
            #如果价格小于10,就校验不通过
            # print(type(data))
            # print(data)
            if float(data)>10:
                return data
            else:
                #校验失败,抛异常
                raise ValidationError('价格太低')
        def validate(self, validate_data):   # 全局钩子
            print(validate_data)
            author=validate_data.get('author')
            publish=validate_data.get('publish')
            if author == publish:
                raise ValidationError('作者名字跟出版社一样')
            else:
                return validate_data
        def update(self, instance, validated_data):
            #instance是book这个对象
            #validated_data是校验后的数据
            instance.name=validated_data.get('name')
            instance.price=validated_data.get('price')
            instance.author=validated_data.get('author')
            instance.publish=validated_data.get('publish')
            instance.save()  #book.save()   django 的orm提供的
            return instance
    
        
     #views.py
    class BookView(APIView):
        def get(self,request,pk):
            book=Book.objects.filter(id=pk).first()
            #用一个类,毫无疑问,一定要实例化
            #要序列化谁,就把谁传过来
            book_ser=BookSerializer(book)  # 调用类的__init__
            # book_ser.data   序列化对象.data就是序列化后的字典
            return Response(book_ser.data)
            # return JsonResponse(book_ser.data)
    
        def put(self,request,pk):
            response_msg={'status':100,'msg':'成功'}
            # 找到这个对象
            book = Book.objects.filter(id=pk).first()
            # 得到一个序列化类的对象
            # boo_ser=BookSerializer(book,request.data)
            boo_ser=BookSerializer(instance=book,data=request.data)
    
            # 要数据验证(回想form表单的验证)
            if boo_ser.is_valid():  # 返回True表示验证通过
                boo_ser.save()  # 报错
                response_msg['data']=boo_ser.data
            else:
                response_msg['status']=101
                response_msg['msg']='数据校验失败'
                response_msg['data']=boo_ser.errors
    
            return Response(response_msg)
    # urls.py
    re_path('books/(?P<pk>d+)', views.BookView.as_view()),

    read_only和write_only

    read_only    表明该字段仅用于序列化输出,默认False,如果设置成True,postman中可以看到该字段,修改时,不需要传该字段
    write_only    表明该字段仅用于反序列化输入,默认False,如果设置成True,postman中看不到该字段,修改时,该字段需要传

    查询所有

    # views.py
    class BooksView(APIView):
        def get(self,request):
            response_msg = {'status': 100, 'msg': '成功'}
            books=Book.objects.all()
            book_ser=BookSerializer(books,many=True)  #序列化多条,如果序列化一条,不需要写
            response_msg['data']=book_ser.data
            return Response(response_msg)
        
    #urls.py
    path('books/', views.BooksView.as_view()),

    新增数据

    # views.py
    class BooksView(APIView):
    
        # 新增
        def post(self,request):
            response_msg = {'status': 100, 'msg': '成功'}
            #修改才有instance,新增没有instance,只有data
            book_ser = BookSerializer(data=request.data)
            # book_ser = BookSerializer(request.data)  # 这个按位置传request.data会给instance,就报错了
            # 校验字段
            if book_ser.is_valid():
                book_ser.save()
                response_msg['data']=book_ser.data
            else:
                response_msg['status']=102
                response_msg['msg']='数据校验失败'
                response_msg['data']=book_ser.errors
            return Response(response_msg)
    #ser.py 序列化类重写create方法
        def create(self, validated_data):
            instance=Book.objects.create(**validated_data)
            return instance
    # urls.py
    path('books/', views.BooksView.as_view()),

    删除一个数据

    # views.pyclass BookView(APIView):    def delete(self,request,pk):        ret=Book.objects.filter(pk=pk).delete()        return Response({'status':100,'msg':'删除成功'})# urls.pyre_path('books/(?P<pk>d+)', views.BookView.as_view()),

    模型类序列化器

    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=Book  # 对应上models.py中的模型
            fields='__all__'
            # fields=('name','price','id','author') # 只序列化指定的字段
            # exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
            # read_only_fields=('price',)
            # write_only_fields=('id',) #弃用了,使用extra_kwargs
            extra_kwargs = {  # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
                'price': {'write_only': True},
            }

    many=True

    # 序列化多条,需要传many=True
    # 
    book_ser=BookModelSerializer(books,many=True)
    book_one_ser=BookModelSerializer(book)
    print(type(book_ser))
    #<class 'rest_framework.serializers.ListSerializer'>
    print(type(book_one_ser))
    #<class 'app01.ser.BookModelSerializer'>
    
    # 对象的生成--》先调用类的__new__方法,生成空对象
    # 对象=类名(name=lqz),触发类的__init__()
    # 类的__new__方法控制对象的生成
    
    
    def __new__(cls, *args, **kwargs):
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        # 没有传many=True,走下面,正常的对象实例化
        return super().__new__(cls, *args, **kwargs)

    Serializer用法

    # source的使用
        1 可以改字段名字  xxx=serializers.CharField(source='title')
        2 可以.跨表publish=serializers.CharField(source='publish.email')
        3 可以执行方法pub_date=serializers.CharField(source='test') test是Book表模型中的方法
        
    
    # SerializerMethodField()的使用
        1 它需要有个配套方法,方法名叫get_字段名,返回值就是要显示的东西
        authors=serializers.SerializerMethodField() #它需要有个配套方法,方法名叫get_字段名,返回值就是要显示的东西
        def get_authors(self,instance):
            # book对象
            authors=instance.authors.all()  # 取出所有作者
            ll=[]
            for author in authors:
                ll.append({'name':author.name,'age':author.age})
            return ll

    自己封装Respons对象

    class MyResponse():
        def __init__(self):
            self.status=100
            self.msg='成功'
        @property
        def get_dict(self):
            return self.__dict__
    
    if __name__ == '__main__':
        res=MyResponse()
        res.status=101
        res.msg='查询失败'
        # res.data={'name':'lqz'}
        print(res.get_dict)
  • 相关阅读:
    使用Linq to Sqlite 出现异常Object already attached
    CSS 嵌套DIV布局
    《面试笔记》——MySQL终结篇(30问与答)
    PotPlayer播放器下载
    博客圆的RSS怎么不能用呢
    OPC在自控系统的应用
    TAPI的使用
    刷iPAQ为Linux(zz HiPDA)
    再论软工
    Silverlight的大小自适应中存在的一个问题
  • 原文地址:https://www.cnblogs.com/zhenghuiwen/p/13266996.html
Copyright © 2011-2022 走看看