zoukankan      html  css  js  c++  java
  • 0801 RESTAPI设计,DRF 序列化

    1.内容回顾
        1.restframework serializer(序列化)的简单使用
            
            QuereySet([obj,obj,obj])  -->  JSON格式数据
            
        安装和导入:
            pip install djangorestframework
            
            from rest_framework import serializer
            
            1.创建一个类,类一定要继承serializer.Serializers
            
            2.chocie字段和FK字段都可以通过source来获取对应的值
            
            3.多对多字段可以通过使用serializer.SerializerMethod
        
                    声明序列化器:
                    def get_tag(self, obj):
                        tag_list = []
                        for i in obj.tag.all():
                            tag_list.append(i.name)
                        return tag_list
                        (比较麻烦)
            2.使用 ModelSerializer(简单)
            ORM绑定的序列化
                通过配置class Meta:
                            model = 表名
                            fields = ['字段', ...]
                            depth = 1
                            
    2. 今日内容
        1. RESTful风格API介绍

    一、什么是RESTful

    REST -- REpresentational State Transfer,英语的直译就是“表现层状态转移”。如果看这个概念,估计没几个人能明白是什么意思。
    那下面就让我来用一句人话解释一下什么是RESTful:URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。

     

    Resource:资源,即数据。

    Representational:某种表现形式,比如用JSON,XML,JPEG等;

    State Transfer:状态变化。通过HTTP动词实现。

    REST与技术无关,代表的是一种软件架构风格,RESTRepresentational State Transfer的简称,中文翻译为表征状态转移

    REST从资源得角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态。

    所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性。

     

    二、RESTful API设计

    API与用户的通信协议
    
    总是使用HTTPs协议域名
    
    https://api.example.com        尽量将API部署在专用域名
    
    https://example.org/api/       API很简单
    
     
    版本
    
    1.  将版本信息放在URL中,如:https://api.example.com/v1/
    
    2. 将版本信息放在请求头中。
    
     
    路径
    
    视网络上任何东西都是资源,均使用名词表示(可复数)
    
    https://api.example.com/v1/zoos
    
    https://api.example.com/v1/animals
    
    https://api.example.com/v1/employees
    
     
    method
    
    GET      :从服务器取出资源(一项或多项)
    
    POST    :在服务器新建一个资源
    
    PUT      :在服务器更新资源(客户端提供改变后的完整资源)
    
    PATCH  :在服务器更新资源(客户端提供改变的属性)
    
    DELETE :从服务器删除资源
    
     
    过滤
    
    通过在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:指定筛选条件
    
     
    状态码
    
    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 错误处理 状态码是4xx时,应返回错误信息,error当做key。     { error: "Invalid API key" }
    返回结果 针对不同操作,服务器向用户返回的结果应该符合以下规范 GET
    /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档 Hypermedia API RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 {"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }} 更多内容,请参考:阮一峰的Blog

    二、基于Django实现RESTful API

     1.RESTful API之url设计

     路由分发:

     在原先的URL中添加:url(r'api/', include(app01_urls)),  include表示导入py文件

    app01下新建路径文件:app01_urls.py

     

    from django.conf.urls import urlfrom app01 import views

     

    urlpatterns = [

        url(r'article/', views.Article.as_view()),

        url(r'article/(?P<pk>d+)', views.ArticleDetail.as_view(), name='article-detail'),

        url(r'school/(?P<id>d+)', views.SchoolDetail.as_view(), name='school-detail111'),

     

        # 评论

        # url(r'comment_list/', ),

        # url(r'add_comment/', ),

        # url(r'delete_comment/', ),

        # url(r'edit_comment/', ),

        url(r'comment/', views.Comment.as_view()),

    ]

     

    2.RESTful API之urls视图

    针对4种请求方式,使用FBV

    def comment(request):

        if request.method == "GET":

            return HttpResponse("获取评论")

        elif request.method == "POST":

            return HttpResponse("创建新评论")

        elif request.method == "PUT":

            return HttpResponse("修改评论")

        elif request.method == "DELETE":

            return HttpResponse("删除评论")

    这样写的话,一个4if判断,就会有很冗长的代码。不利与维护,推荐使用CBV方式

    CBV方式

    from django import viewsclass Comment(views.View):

        def get(self, request):

            return HttpResponse("获取评论")

     

        def post(self, request):

            return HttpResponse("创建新评论")

     

        def put(self, request):

            return HttpResponse("修改评论")

     

        def delete(self, request):

            return HttpResponse("删除评论")

    三、 APIView

     from rest_framework import APIView  
     class Comment(views.View):

               pass     

        --------  之前的写法 ↑  ----------

        class APIView(views.View):

            扩展的功能

            self.request = Resquest(

                self._request = request(Django的request)

            )

            self.request.data  封装好的数据属性,POSTPUT请求携带的数据都可以从这里取

            pass

     

      class Comment(APIView):

            pass

    1. APIView与View的区别

    1. APIViewView的子类
    2. 传递给请求处理程序的request实例是REST框架的请求实例,而不是DjangoHttpRequest实例
    3. 处理程序返回的基于REST框架的Response,而不是DjangoHttpResponse,视图函数将会管理内容协商,然后设置正确的渲染方式
    4. 任何APIException将会被捕捉,然后转换成合适的response对象
    5. 接收到的请求首先被认证,然后赋予相应的权限,然后通过节流器分发给相应的请求处理函数,类似.get().post()

     

    APIView是专门写API的视图函数,结合serializers,非常方便做序列化!

    关于APIView的源码解析,请参考文章:

    https://blog.csdn.net/u013210620/article/details/79857654

     

    取数据

    get:

      request._request ( 原request )

      request.GET == request._request.GET 

    post:

      request.data ( 得到 json 数据,也可得到 urlencode 数据)

        都是 rest_framework  APIView 的功劳,

      如果使用 View , request.post( 只能得到 urlencode 的数据,不能得到 json 数据,只能从 request.body 中取) 

     3. serializer
            1. ModelSerializer
                1. 基于APIView实现的GETPOST

        2. POST过来的数据进行校验
                3. 保存数据

    在app01下面创建一个文件,专门放序列化的
    
    from app01.models import Comment, Article, School
    
    from rest_framework import serializers
    
    from rest_framework.validators import ValidationError
    
     # 序列化评论的类
      class CommentSerializer(serializers.ModelSerializer):
    # content = serializers.CharField(error_messages={"required": '内容不能为空'}) # article_detail = serializers.SerializerMethodField(read_only=True) # 用于做校验的钩子函数,类似于form组件的clean_字段名 def validate_content(self, value): if '' in value: raise ValidationError('不符合社会主义核心价值观!') else: return value # 全局的钩子 # def validate(self, attrs): # # self.validated_data # 经过校验的数据 类似于form组件中的cleaned_data # # 全局钩子 # pass class Meta: model = Comment fields = "__all__" # depth = 1 # 定义额外的参数 extra_kwargs = { "content": { "error_messages": { "required": '内容不能为空', } }, "article": { "error_messages": { "required": '文章不能为空' } } } # 文章的序列化类
      class ArticleModelSerializer(serializers.ModelSerializer):
    # type_str = serializers.CharField(source='get_type_display', read_only=True) class Meta: model = Article # 绑定的ORM类是哪一个 fields = "__all__" # ["id", "title", "type"] # depth = 1 # 官方推荐不超过10层 # 文章超链接序列化 class ArticleHyperLinkedSerializer(serializers.HyperlinkedModelSerializer): school = serializers.HyperlinkedIdentityField(view_name='school-detail111', lookup_url_kwarg='id') class Meta: model = Article # 绑定的ORM类是哪一个 fields = ["id", "title", "type", "school"] # 学校的序列化 class SchoolSerializer(serializers.ModelSerializer): class Meta: model = School fields = "__all__" 参数解释: source 表示来源 lookup_field 表示查找字段 lookup_url_kwarg 表示路由查找的参数,pk表示主键 view_name 它是指urls定义的name值,一定要一一对应。

    models.py

    from django.db import models

    # 文章表
    class Article(models.Model):
        title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章标题不能重复"})
        # 文章发布时间
        # auto_now每次更新的时候会把当前时间保存
        create_time = models.DateField(auto_now_add=True)
        # auto_now_add 第一次创建的时候把当前时间保存
        update_time = models.DateField(auto_now=True)
        # 文章的类型
        type = models.SmallIntegerField(
            choices=((1, "原创"), (2, "转载")),
            default=1
        )
        # 来源
        school = models.ForeignKey(to='School', on_delete=models.CASCADE)
        # 标签
        tag = models.ManyToManyField(to='Tag')

    # 文章来源表
    class School(models.Model):
        name = models.CharField(max_length=16)

    # 文章标签表
    class Tag(models.Model):
        name = models.CharField(max_length=16)

    # 评论表
    class Comment(models.Model):
        content = models.CharField(max_length=128)
        article = models.ForeignKey(to='Article', on_delete=models.CASCADE)


    GET:
    class Comment(APIView):

     

        def get(self, request):

            res = {"code": 0}

            # print(self.request)  # rest framework封装好的request

            # print(self.request.data)

            # print(self.request._request.GET.get("age"))  # Django原来的request对象

            # 数据

            all_comment = models.Comment.objects.all()

            # 序列化

            ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)

            res["data"] = ser_obj.data

            return Response(res)

    POST:
    def post(self, request):
        res = {"code": 0}
        # 去提交的数据
        comment_data = self.request.data
        # 对用户提交的数据做校验
        ser_obj = app01_serializers.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示数据没问题,可以创建
            ser_obj.save()  #创建数据,写入数据库
        else:
            # 表示数据有问题
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

        return HttpResponse("创建新评论")

    #文章CBV
    class Article(APIView):
      def get(self,request):
        res={"code":0}
        all_article = models.Article.object.all()                        #显示超链接的URL需要的参数,表示上下文
        ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article, many=True, context={'request': request})
        res["data"] = ser_obj.data
          return Response(res)

      def post(self, request):
          res = {"code": 0}
          ser_obj = app01_serializers.ArticleModelSerializer(data=self.request.data)
          if ser_obj.is_valid():
              ser_obj.save()
          else:
              res["code"] = 1
              res["error"] = ser_obj.errors
          return Response(res)

    # 文章详情CBV
    class ArticleDetail(APIView):
        def get(self, request, pk):
            res = {"code": 0}
            article_obj = models.Article.objects.filter(pk=pk).first()
            # 序列化
            ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request})
            res["data"] = ser_obj.data
            return Response(res)


    # 学校详情CBV
    class SchoolDetail(APIView):
        def get(self, request, id):
            res = {"code": 0}
            school_obj = models.School.objects.filter(pk=id).first()
            ser_obj = app01_serializers.SchoolSerializer(school_obj, context={'request': request})
            res["data"] = ser_obj.data
            return Response(res)

     

    Response: 

       

    配置之后:浏览器才可访问; rest_framework django的一个app

    可发 get / post 请求 !!

     

    rest framework 引入了Response对象,它是一个TemplateResponse类型,并根据客户端需求正确返回需要的类型。

     

    使用前,需要导入模块

     

    from rest_framework.response import Response

     

     

     

    语法:

     

    return Response(data) # 根据客户端的需求返回不同的类型。

     

  • 相关阅读:
    hdu 5723 Abandoned country 最小生成树 期望
    OpenJ_POJ C16G Challenge Your Template 迪杰斯特拉
    OpenJ_POJ C16D Extracurricular Sports 打表找规律
    OpenJ_POJ C16B Robot Game 打表找规律
    CCCC 成都信息工程大学游记
    UVALive 6893 The Big Painting hash
    UVALive 6889 City Park 并查集
    UVALive 6888 Ricochet Robots bfs
    UVALive 6886 Golf Bot FFT
    UVALive 6885 Flowery Trails 最短路
  • 原文地址:https://www.cnblogs.com/Mr-Murray/p/9402305.html
Copyright © 2011-2022 走看看