• 认识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. 将QT开发的界面程序封装成DLL,在VC中成功调用(必须有消息循环,所以使用了QTWinmigrate,附CSDN可下载的Demo)

    最近手头的一个项目需要做一个QT界面,并且封装成DLL,然后再动态调用DLL给出的接口函数,使封装在DLL内部的QT界面跑起来,在网上查了很多资料,今天终于成功了,经验不敢独享,因为CSDN给了我很多 ...

  2. logstash performance testing

    最近一直在和peformance team的同事做logstash 5.6.2的测试,主要测试两个方面:一方面测试log数据是否能全部被logstash获取与发出去,一方面测试logstash自身的c ...

  3. 15个非常棒的jQuery无限滚动插件【瀑布流效果】

    现在,最热门的网站分页趋势之一是jQuery的无限滚动(也即瀑布流).如果你碰巧观察Pinterest的网站,如Facebook,Twitter和deviantART的,你会发现无限滚动的动作,当旧的 ...

  4. Django学习笔记(20)——BBS+Blog项目开发(4)Django如何使用Bootstrap

    本文学习如何通过Django使用Bootstrap.其实在之前好几个Django项目中已经尝试使用过了Bootstrap,而且都留有学习记录,我已经大概有了一个大的框架,那么本文就从头再走一遍流程,其 ...

  5. springboot 集成完整的swagger2

    springboot 在集成swagger中会不会遇到各种问题: 1.swagger  进行接口鉴权(比如设置header的token,接口进行拦截处理). 2.swagger 进行实体属性解析(po ...

  6. 戏说 .NET GDI+系列学习教程(一、Graphics类--纸)

    Graphics类(纸) Graphics类封装一个GDI+绘图图面,提供将对象绘制到显示设备的方法,Graphics与特定的设备上下文关联. 画图方法都被包括在Graphics类中,在画任何对象时, ...

  7. 关于Jvm类加载机制,这一篇就够了

    前言 一个月没更新了,这个月发生了太多的事情,导致更新的频率大大降低,不管怎样收拾心情,技术的研究不能落下! jvm作为每个java程序猿必须了解的知识,博主推荐一本书<深入理解Java虚拟机& ...

  8. CentOS 7编译安装Redis

    安装编译相关依赖 CentOS/Fedora yum install gcc gcc-c++ cmake tcl 下载Redis源码包 curl -o redis-4.0.10.tar.gz http ...

  9. 通用shell函数库

    1.输出字体颜色库 #!/bin/bash export black='\E[0m\c' export boldred='\E[1;31m\c' export boldgreen='\E[1;32m\ ...

  10. 谈谈NOSQL

    从MongoDB引到NOSQL 要讲MongoDB之前,首先要提到一个概念NOSQL(NoSQL = Not Only SQL ) 很大一部分数据是由关系型数据库管理系统(RDMBSs)来处理的,关系 ...