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) # 根据客户端的需求返回不同的类型。

     

  • 相关阅读:
    datepicker防手动输入
    [ACM]Link-Cut Tree实现动态树初探
    STL priority_queue 优先队列 小记
    hihoCoder挑战赛1 毁灭者问题
    python编程技巧
    openstack horizon 学习(3) DataTable
    Upcasting, downcasting in JAVA
    SGU 145
    URAL 1003,1004
    自建物流的无人机实验(困难)
  • 原文地址:https://www.cnblogs.com/Mr-Murray/p/9402305.html
Copyright © 2011-2022 走看看