• 认识RESTful

    • REST是设计风格而不是标准,简单来讲REST规定url是用来唯一定位资源,而http请求方式则用来区分用户行为.

    • REST接口设计规范

      • HTTP常用动词

        • GET /books:列出所有书籍 返回数据类型-->[{},{}]

        • GET /books/ID:获取某个指定书籍的信息 -->{单本书籍}

        • POST /books:新建一本书籍 -->{新增的数据}

        • PUT /books/ID:更新某本指定书籍的信息(提供该书籍的全部信息) -->{修改后的数据}

        • PATCH /books/ID:更新某本指定书籍的信息(提供该书籍的部分信息) -->{修改后的数据}

        • DELETE /books/ID:删除某本书籍 -->返回空

      • 错误消息规范

        • { error: "Invalid API key" }

  • APIView请求流程(源码剖析)

    • from rest_framework.views import APIView(导入APIView)

    • 启动django后,加载settings及各个组件(视图,路由等)

    • 加载的时候urls中views.HomeView.as_view(),会执行as_view()类方法,这里APIView继承了View的as_view方法,所以最后返回的还是view函数,实际上此时django内部就已经帮我们做了一层url和视图函数的关系绑定

    • 以访问首页为例,找到url对应的视图函数,执行view(request),返回APIView中的dispatch()并执行该方法

    • 找到对应的get或者post等方法并执行,最终将结果返回给浏览器

  • 解析器组件

    • 解析器组件的使用

      • request.data(通过该方法得到处理完的字典数据)

    • 解析器请求流程(源码剖析)

      • 简单的讲解析器就是根据不同的content-type来解析数据,最终返回字典格式的数据

      • 在执行view函数时,首次将request传入,执行APIView中的dispatch()方法

      • 在dispatch()中将原来的request对象传递给初始化函数: request = self.initialize_request(request, *args, **kwargs)

      • 执行APIView中的initialize_request,返回Request(request,parsers=self.get_parsers(),..)

      • from rest_framework.request import Request,从这里可以看出,Request是一个类,所以Request()是一个实例化对象

      • 而我们通过request.data触发解析方法,此时调用Request实例的data方法(这里用了@property)

      • 在data方法里面,执行self.load_data_and_files(),此时self指的是Request的实例化对象,所以执行Request中的load_data_and_files()方法

      • load_data_and_files中,执行Request中的self.parse()方法

      • 在self._parse()方法中,首先media_type = self.content_type,获取了content_type,并执行了parser = self.negotiator.select_parser(self, self.parsers),这里传入了self.parsers,而Request实例化的时候传入了parsers=self.get_parsers(),所以这里要看下self.get_parsers()该方法

      • 在APIView中执行get_parsers()方法,return [parser() for parser in self.parser_classes],返回了一个列表推导式,而parser_classes = api_settings.DEFAULT_PARSER_CLASSES,那api_settings是啥呢?

      • from rest_framework.settings import api_settings,从这里我们可以知道是从settings中导入的,在settings中api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS),APISettings是一个类,所以api_settings是APISettings的实例化对象

      • 实例化对象后,要执行api_settings.DEFAULT_PARSER_CLASSES,但是APISettings类中并没有该属性或方法,则执行getattr方法, if attr in self.import_strings: val = perform_import(val, attr),这里动态导入,最后返回的是parser_classes 就等于[<class 'rest_framework.parsers.JSONParser'>,<class 'rest_framework.parsers.FormParser'>, <class 'rest_framework.parsers.MultiPartParser'>]

      • parser() for parser in self.parser_classes中parser()是实例化各个类,parsers=self.get_parsers(),parser = self.negotiator.select_parser(self, self.parsers),所以最后这个一执行就触发前面的一步一步执行

      • 最终self.data, self.files = self.parse(),将self.parse()的返回值赋给self.data,再将self.full_data = self.data,最终返回了self.full_data

  • 序列化组件

    • django原生序列化步骤

      • 导入模块 from django.core.serializers import serialize

      • 获取queryset (data = Book.objects.all())

      • 对queryset进行序列化 serialize_data = (serialize("json", data))

      • 将序列化之后的数据,响应给客户端,返回serialize_data

    • APIView说明
      • APIView继承了View,并在内部将request重新赋值变成了Request()的实例化对象,因此继承APIView后

      • 获取get请求数据要通过,request.query_params

      • 获取post请求要通过request.data

    • 序列化组件使用

      • Get接口设计(序列化):
        • 声明序列化器

          • 导入模块:from rest_framework import serializers

          • 建立一个序列化类 (写在一个独立的文件中): class BookSerializer(serializers.Serializer):字段自定义,最好跟model的一致

            from rest_framework import serializers
            
            class PublishSerializer(serializers.Serializer):
            id = serializers.IntegerField()
            name = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer):
            id = serializers.IntegerField()
            name = serializers.CharField(max_length=32) class BookSerializer(serializers.Serializer):
            id = serializers.IntegerField()
            title = serializers.CharField(max_length=32)
            # 字段中的通用属性source可以进行数据库操作,get_xxx_display取choice字段的value.(source只能用于序列化)
            category = serializers.CharField(source="get_category_display")
            pub_time = serializers.DateField()
            # 外键,通过id找到publish对象并传入PublishSerializer(publish对象),最终得到id和title,因为这里返回的是单个对象,所以不用写many,而多对多返回的是一个queryset所以多个需要写many=True,内部会循环遍历
            publish = PublishSerializer()
            # 多对多,记得有many=True
            authors = AuthorSerializer(many=True)
        • 使用自定义的序列化器序列化queryset(将模型对象放入序列化器进行字段匹配,匹配上的就进行序列化,匹配不上的就丢弃)

        • 序列化完成的数据在序列化返回值的data中(serializer_data.data)

          from django.shortcuts import render,HttpResponse
          from django.views import View
          from django.http import JsonResponse import json from rest_framework.views import APIView
          from rest_framework.response import Response from .models import Book
          from .serializerses import BookSerializer class BookView(APIView):
          """书籍相关视图"""
          def get(self, request):
          book_queryset = Book.objects.all()
          # 使用自定义的序列化器序列化queryset
          serializer_data = BookSerializer(book_queryset, many=True)
          # 注意这里是取序列化返回值.data
          return Response(serializer_data.data)
          注意:外键关系的序列化是嵌套的序列化对象,多对多有many=True,还有如果需要在浏览器访问请求的话需要在settings中INSTALLED_APPS下加入'rest_framework'.
        • 上面是get所有数据,如果是请求单条数据,urls可以设计book/1,这样的话需要重新定义View,序列化的时候因为是单个book_obj,所以也不用写many=True了,默认是False

      • Post接口设计(反序列化):
        • 步骤如下:

          • 首先先确定新增的数据结构(跟前端沟通达成一致)

          • 定义序列化器

            • 正序和反序字段不统一,部分需要重新定义

            • required=False, 只序列化不走校验

            • read_only=True, 只序列化用

            • write_only=True, 只反序列化用

            • 重写create方法,参数有验证通过的数据validated_data

          • 验证通过返回ser_obj.validated_data,不通过则返回ser_obj.errors

          • 还有些细节注意在代码中,请看下面代码.

          serializerses.py文件内容

          from rest_framework import serializers
          
          from app01.models import Book
          
          class PublishSerializer(serializers.Serializer):
          id = serializers.IntegerField()
          name = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer):
          id = serializers.IntegerField()
          name = serializers.CharField(max_length=32) class BookSerializer(serializers.Serializer):
          id = serializers.IntegerField(required=False)
          title = serializers.CharField(max_length=32)
          # 字段中的通用属性source可以进行数据库操作,get_xxx_display取choice字段的value
          category = serializers.CharField(source="get_category_display", read_only=True)
          pub_time = serializers.DateField()
          # 外键
          publish = PublishSerializer(read_only=True)
          # 多对多,记得有many=True
          authors = AuthorSerializer(many=True, read_only=True) category_id = serializers.IntegerField(write_only=True)
          publish_id = serializers.IntegerField(write_only=True)
          # 多对多传入的是[],所以用ListField
          author_list = serializers.ListField(write_only=True) # 将通过验证的validated_data数据插入数据库
          def create(self, validated_data):
          # 先将作者列表从字典中剔除
          author_list = validated_data.pop('author_list')
          book_obj = Book.objects.create(title=validated_data['title'],category=validated_data['category_id'], pub_time = validated_data['pub_time'],publish_id=validated_data['publish_id'])
          book_obj.authors.add(*author_list)
          return book_obj

          views.py内容

          from django.shortcuts import render,HttpResponse
          
          from rest_framework.views import APIView
          from rest_framework.response import Response from .models import Book
          from .serializerses import BookSerializer
          '''
          前端提交的格式如下:
          {
          "title": "西游1",
          "category_id": 3,
          "pub_time": "2019-04-22",
          "publish_id": 1,
          "author_list": [1,2]
          }
          ''' class BookView(APIView):
          """书籍相关视图"""
          def get(self, request):
          book_queryset = Book.objects.all()
          serializer_data = BookSerializer(book_queryset, many=True)
          return Response(serializer_data.data) def post(self, request):
          # 获取前端提交的请求数据
          book_obj = request.data
          # 通过data来表示是反序列化
          ser_obj = BookSerializer(data=book_obj)
          # 数据校验
          if ser_obj.is_valid():
          # save()方法返回self.instance = self.create(validated_data)
          # 然后就会调用BookSerializer中create方法保存通过校验的数据
          # 最后 save()会返回self.instance,而self.instance就是create的返回数据book_obj(插入的书本对象)
          ser_obj.save()
          # 给前端返回新增成功的数据
          return Response(ser_obj.validated_data)
          return Response(ser_obj.errors)
        • Put接口和单个get设计:
          • 通过book/id去请求

          在views中,记得put需要传入要修改的book对象,还有修改的数据和设置部分字段校验partial=True.并且定义update方法

          class BookEditView(APIView):
          def get(self, request, book_id):
          book_obj = Book.objects.filter(id=book_id).first()
          ser_obj = BookSerializer(book_obj)
          return Response(ser_obj.data) def put(self, request, book_id):
          book_obj = Book.objects.filter(id=book_id).first()
          # 针对单本书籍对象进行修改,所以instance是单本书籍对象,data是传入的书籍信息,partial为True表示只校验提交的字段,也就是部分字段校验
          ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
          if ser_obj.is_valid():
          ser_obj.save()
          return Response(ser_obj.validated_data)
          return ser_obj.errors

          在序列化器中定义update方法

           def update(self, instance, validated_data):
          # instance表示更新的book_obj对象,validated_data是校验通过的数据
          # 给每一个字段逐一赋值,如果没有就用原来的值
          instance.title = validated_data.get('title', instance.title)
          instance.category = validated_data.get('category_id', instance.category)
          instance.pub_time = validated_data.get('pub_time', instance.pub_time)
          instance.category = validated_data.get('category_id', instance.category)
          if validated_data.get('author_list'):
          instance.authors.set(validated_data.get('author_list'))
          # 基于对象操作,保存需要save一下
          instance.save()
          return instance
        • 序列化接口字段校验
           # 类似于form局部钩子验证,针对单个属性
          def validate_title(self, value):
          # 对书籍标题进行验证,假设标题不能为纯数字
          if value.isdigit():
          raise serializers.ValidationError("标题不能为纯数字!")
          return value # 多个属性校验
          def validate(self, attrs):
          # 对标题和出版社进行校验
          if attrs['title'].isdigit() or not attrs['publish_id'].isdigit():
          raise serializers.ValidationError("标题不能为纯数字,出版社必须是数字")
          return attrs

          上面是单个属性和多个属性校验,下面还有个自定义校验,然后绑定到字段上的用法

          def my_validata(value):
          # 自定义规则
          if '敏感信息' in value:
          raise serializers.ValidationError("标题不能包含敏感信息")
          return value
          # 通过validators使用,可以有多个自定义校验规则
          title = serializers.CharField(max_length=32, validators=[my_validata,])

          有没有发现上面的序列化和反序列化都非常繁琐,所以我们在真实开发中一般都用下面的这种

        • ModelSerializer序列化及反序列化

          modelSerializers文件内容

          from rest_framework import serializers
          from app01.models import (
          Book, Publish, Author
          ) class PublishModelSerializer(serializers.ModelSerializer):
          class Meta:
          model = Publish
          fields = "__all__" class AuthorModelSerializer(serializers.ModelSerializer):
          class Meta:
          model = Author
          fields = "__all__" class BookModelSerializer(serializers.ModelSerializer):
          # 这里定义的都是序列化的情况,所以都是read_only=True,而且都是通过自定义方法返回需要展示的字段
          category_info = serializers.SerializerMethodField(read_only=True)
          publish_info = serializers.SerializerMethodField(read_only=True)
          authors_info = serializers.SerializerMethodField(read_only=True) # get_字段名称,这个是固定搭配,该方法会return值给category_info字段,而且传入的是序列化的obj模型对象
          def get_category_info(self, obj):
          # 自定义需要展示的数据,这里展示课程的中文, choices类型
          return obj.get_category_display() def get_publish_info(self, obj):
          # 以字典形式展示的是出版社的id和name, 外键类型
          publish_obj = obj.publish
          return {"id":publish_obj.id, "name":publish_obj.name} def get_authors_info(self, obj):
          # 由于作者可能有多个,所以以列表包含多个字典形式展示, 多对多类型
          authors_querset = obj.authors.all()
          return [{"id":author.id, "name":author.name} for author in authors_querset] # 反序列化用原生的,部分序列化字段需要重新定义,记得不要和原生字段重名
          class Meta:
          model = Book
          fields = "__all__"
          # exclude = ["id"] # 不包含id,但是fields和exclude只能有一个
          # 字段是有序的,根据下面的字段进行排序展示
          # fields = ["id", "pub_time","title", "category_info", "publish_info", "authors_info"]
          # depth=1是深度为1层,也就是把外键关系的第一层找出来,默认不写是不显示出版社名称和作者信息的,写了就找出了出版社的所有信息和作者所有信息
          # 注意:depth会让所有的外键关系字段都变成read_only=True,所以一般少用
          # depth = 1
          read_only_fields = ["id", ] # 指明只读字段,可以写多个
          # 添加或修改原有参数,反序列化的个别字段只需要在反序列化看到
          extra_kwargs = {
          'category': {"write_only": True,}, # key为默认的字段名称, value是自定义的参数配置信息
          'publish': {"write_only": True},
          'authors': {"write_only": True},
          }

          views文件:(跟之前一样)

        • from rest_framework.views import APIView
          from rest_framework.response import Response from .models import Book
          from .modelSerializers import BookModelSerializer class BookView(APIView):
          """书籍相关视图"""
          def get(self, request):
          book_queryset = Book.objects.all()
          serializer_data = BookModelSerializer(book_queryset, many=True)
          return Response(serializer_data.data) def post(self, request):
          # 获取前端提交的请求数据
          book_obj = request.data
          # 通过data来表示是反序列化
          ser_obj = BookModelSerializer(data=book_obj)
          # 数据校验
          if ser_obj.is_valid():
          # save()方法返回self.instance = self.create(validated_data)
          # 然后就会调用BookSerializer中create方法保存通过校验的数据
          # 最后 save()会返回self.instance,而self.instance就是create的返回数据book_obj(插入的书本对象)
          ser_obj.save()
          # 给前端返回新增成功的数据
          return Response(ser_obj.validated_data)
          return Response(ser_obj.errors) class BookEditView(APIView):
          def get(self, request, book_id):
          book_obj = Book.objects.filter(id=book_id).first()
          ser_obj = BookModelSerializer(book_obj)
          return Response(ser_obj.data) def put(self, request, book_id):
          book_obj = Book.objects.filter(id=book_id).first()
          # 针对单本书籍对象进行修改,所以instance是单本书籍对象,data是传入的书籍信息,partial为True表示只校验提交的字段,也就是部分字段校验
          ser_obj = BookModelSerializer(instance=book_obj, data=request.data, partial=True)
          if ser_obj.is_valid():
          ser_obj.save()
          return Response(ser_obj.validated_data)
          return Response(ser_obj.errors) def delete(self, request, book_id):
          book_obj = Book.objects.filter(id=book_id).first()
          if not book_obj:
          return Response("要删除的对象不存在")
          book_obj.delete()
          return Response("")

Django REST Framework(DRF)_第一篇的更多相关文章

  1. Django REST Framework(DRF)_第二篇

    视图和路由 视图封装 第一次封装 ​ 上一篇最后我们对书籍表做了增删改查,那么如果现在我们有几十上百张表需要这样做呢?我们知道类的特性有封装,因此我们可以尝试进行封装下. from rest_fram ...

  2. Django REST Framework(DRF)_第四篇

    DRF分页(总共三种) PageNumberPagination(指定第n页,每页显示n条数据) 说明 既然要用人家的那么我们就先来看下源码,这个分页类源码中举例通过参数指定第几页和每页显示的数据:h ...

  3. Django REST Framework(DRF)_第三篇

    DRF版本控制 介绍 我们在看APIView源码时可以看到,版本和版本控制类是通过determine_version的返回值获取的 version, scheme = self.determine_v ...

  4. Django 学习之Django Rest Framework(DRF)

    一. WEB应用模式 在开发Web应用中,有两种应用模式 1. 前后端不分离 把html模板文件和django的模板语法结合渲染完成以后才从服务器返回给客户. 2. 前后端分离 二. API接口 AP ...

  5. day71:drf:API接口&Restful API规范&Django Rest Framework&drf中的序列化和反序列化功能

    目录 1.web应用模式 2.API接口 3.Restful API规范 4.序列化 5.Django Rest Framework 1.drf的简单介绍 2.drf的特点 3.如何安装drf 4.d ...

  6. Python全栈开发记录_第一篇(循环练习及杂碎的知识点)

    Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...

  7. Matlab高级教程_第一篇:Matlab基础知识提炼_01

    第一篇:Matlab基础知识提炼: 这一篇主要用系统和提炼性的语言对Matlab基础知识进行总结,主要适用于有语言基础的学习者.尽量不讲废话. 第一部分:Matlab是什么? 1 Matlab是Mat ...

  8. Django(二)框架第一篇基础

    https://www.cnblogs.com/haiyan123/p/7701412.html 一个小问题: 什么是根目录:就是没有路径,只有域名..url(r'^$') 补充一张关于wsgiref ...

  9. Python笔记_第一篇_面向过程_第一部分_0.开场白

    *什么是Python? Python是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido(吉多) van Rossum于1989年发明,第一个公开版本发行于1991年.在国外应用非常的广泛,国 ...

随机推荐

  1. OpenSSL所有版本的变化,从1.1开始架构有所变化,生成的lib名称也有所不同了,以及对Qt的影响

    The complete explanation is that 1.0.x and 1.1.x do not have the same naming conventions for the gen ...

  2. python列表的内建函数

    list.append(obj) 向列表中添加一个对象obj list.count(obj) 返回一个对象obj 在列表中出现的次数 list.extend(seq)a 把序列seq 的内容添加到列表 ...

  3. 查看linux系统时间和时区

    参考地址:http://lidao.blog.51cto.com/ 一.使用date命令查看系统时间 [root@benbang ~]# date -R Tue, 01 Aug 2017 15:43: ...

  4. Django ORM基础篇【转载】

    ORM( Object relational mapping 对象关系映射)D:把面向对象中的类和数据库表一一对应起来,在django项目与数据库之间起着桥梁的                     ...

  5. 浅谈IHttpHandler

    在Web应用开发或接口开发时,处理请求接口IHttpHandler随处可见,那么我们这次来简单聊一下这个接口. ASP.NET响应Http请求时常用的两个处理接口,分别是IHttpHandler和IH ...

  6. mysql group_concat的长度问题

    mysql group_concat的长度问题 show variables like 'group_concat_max_len';+----------------------+-------+| ...

  7. java获取Timestamp类型的当前系统时间

    java获取取得Timestamp类型的当前系统时间 java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = new ...

  8. 如何正确选择挑选适合的VPS服务器

    就来讲讲,如何挑选适合你的VPS.基本过程就是:1.你使用VPS的用途:2.你需要的线路:3.你要选择的操作系统:4.你购买VPS的大概预算是多少. 一.用途方法,其实买VPS就是:建站.VPN使用. ...

  9. kafka迁移topic

    1. 准备移动 这里假设要移动的topic名称为:topicA.topicB vim topics-to-move.json {"topics": [{"topic&qu ...

  10. 提升——树形DP

    这里讲提高一点的内容,所以没有树形DP基础的,先看一下基础部分: 浅说——树形DP 闲言不表,看第一题. 这道题是典型的树上最长链问题.(就是一个模板题) 给定一棵树,树上共有N个节点(N<=5 ...