Loading

DRF入门

Django请求生命周期流程图

image

下载安装

  • pip3 install djangorestframework
  • pycharm下载
  • drf安装默认安装的最新版本,如果django版本过低会自动升级到3.x版本
  • 版本支持对应关系
    image

restful规范

前后端交互使用api接口

符合某种规范

restful规范
	定义:restful定义了web api接口的设计风格,尤其适用于前后端分离的应用模式中

前后端分离的标准
	
	1.保证数据的安全
	    API与用户的通信协议,总是使用HTTPs协议。
	2.用api关键字标识接口
	    https://api.example.com	尽量将API部署在专用域名(会存在跨域问题)
	    https://example.org/api/	API很简单
	3.多版本共存---》在接口地址中带版本号
	    URL				如:https://api.example.com/v1/
	    请求头			跨域时,引发发送多次请求
	4.路径,视网络上任何东西都是资源,均使用名词表示(可复数)
	    https://api.example.com/v1/animals
	5.操作资源由请求方式决定:method
	    GET:从服务器取出资源(一项或多项)
	    POST:在服务器新建一个资源
	    PUT:在服务器更新资源(客户端提供改变后的完整资源)
	    PATCH:在服务器更新资源(客户端提供改变的属性)
	    DELETE:从服务器删除资源
	6.过滤,通过在url上传参的形式传递搜索条件
	    https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
	    https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
	7.状态码
	    HTTP状态响应码(200、403、404)
	    json数据自带状态码,服务端自定义的
	8.错误处理,应返回错误信息,error当做key。
	    {
	        error: "Invalid API key"
	    }
	9.返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
	    GET /collection:返回资源对象的列表(数组)
	    GET /collection/resource:返回单个资源对象
	    POST /collection:返回新生成的资源对象
	    PUT /collection/resource:返回完整的资源对象
	    PATCH /collection/resource:返回完整的资源对象
	    DELETE /collection/resource:返回一个空文档
	10.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"
	    }}
  • HTTP状态响应码
    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
    

序列化和反序列化

API接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:

  • 序列化:把我们语言识别的数据转换成指定的格式提交给别人(前端)
    • 比如python中的字典,列表,对象等转json,xml,prop····
  • 反序列化:把别人提供的数据转换成我们所需的格式
    • 最常见的比如我们使用json模块来对数据进行处理····

在Djangorestframework中的序列化反序列化又是如何?

  • 序列化: 在Django中获取到的数据默认是模型对象(QuerySet对象),但是模型对象数据无法直接提供给前端或别的平台使用,我们需要把数据进行序列化,变成字符串或json数据提供给前端或其他平台;
  • 反序列化: 前端或其他平台传入数据到后台,比如是json格式字符串,后端需要存入数据库,需要转换成python中的对象,然后处理存入数据库;

drf快速使用

快速写5个接口
使用Django写五个接口得配5个路由,5个视图函数去处理,现在使用drf不需要了,如下:

-查询所有---》get->http://127.0.0.1:8000/books/
-查询一个---》get->http://127.0.0.1:8000/books/1/
-新增一个---》post->http://127.0.0.1:8000/books/  # 在body中带数据
-修改-----》put,patch--->实际编码中,基本都用put,全量修改;patch改部分
  	http://127.0.0.1:8000/books/1/ body体中传入修改的数据
-删除一个---》delete-->http://127.0.0.1:8000/books/1/

'''所有的接口都是这五个的变形'''

登录本质就是查询一个,注册本质就是新增一个
'''
在我们使用postman的时候,地址严格写,不能缺少/
'''
  • models.py
    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=5,decimal_places=2)
        authors = models.CharField(max_length=32)
    
  • views.py
    from rest_framework.viewsets import ModelViewSet
    from app import models
    from app.serializer import  BookSerializer
    class BookView(ModelViewSet):
        serializer_class =BookSerializer
        queryset = models.Book.objects.all()
    
  • serializer.py
    from rest_framework.serializers import ModelSerializer
    from app import models
    class BookSerializer(ModelSerializer):
        class Meta:
            model = models.Book
            fields = '__all__'
    
  • urls.py
    from rest_framework.routers import SimpleRouter
    from app import views
    
    router = SimpleRouter()
    router.register('books', views.BookView)
    '''
    register(prefix, viewset, base_name)
    prefix 该视图集的路由前缀
    viewset 视图集
    base_name 路由名称的前缀
    '''
    urlpatterns = [
        path('admin/', admin.site.urls),
    ]
    urlpatterns += router.urls
    
    # 千万注意别把注释写到urlpatterns列表中,那样就不是注释了,成字符串了!!!
    
  • 在settings的app中注册
    INSTALLED_APPS = [
        'rest_framework'
    ]
    

CBV源码流程分析

因为DRF框架里大部分都是基于CBV(视图类)写,所以流程是什么,如何执行需要了解,同时也方便理解APIView,顺便提一嘴APIView也是继承了View ----> class APIView(View)
这里需要强调一下,CBV路由归根结底还是FBV都是函数的内存地址,比如views.类.as_view()底层仍然是函数的内存地址

  • CBV源码执行流程
    '''views.py'''
    from django.views import View
    from django.http import HttpResponse
    
    class TestView(View):
        def get(self,request):
            return HttpResponse('get请求')
        # post csrf认证注释掉就好了
        def post(self,request):
            return HttpResponse('post请求')
    
    '''urls.py'''
    path('test/',views.TestView.as_view())
    
  • 写一个视图类,写了get方法和post方法,来了get请求就走get方法,来了post请求就走post方法,过程如何?且看分析源码执行过程~
    '''请求来了在不考虑中间件的情况下,从路由的匹配关系和视图函数来看'''
    1、cbv路由写法:path('test/', views.TestView.as_view())
    # 第二个参数是函数内存地址,CBV的底层也是FBV,as_view是类的绑定方法,自己的类中没有去父类(View)找,as_view()执行完,也是一个内存地址,内存地址是谁的?是闭包函数view的如下源码👇
    @classonlymethod
        def as_view(cls, **initkwargs):
            ···
            def view(request, *args, **kwargs):
                ···
                return self.dispatch(request, *args, **kwargs)
            ···
            return view
    # @classonlymethod通过描述符自定义装饰器
    
    2、请求来了,路由也匹配成功,执行上边返回的view(requets),加括号调用,并且传入了当次请求的request对象
    
    3、然后又返回了当前对象的dispatch方法,自己的名称空间内没有,那么去父类中找,然后发现父类(View)的dispatch方法返回了return 	handler(request, *args, **kwargs)
    
    4、dispatch方法处理请求,什么请求对应什么方法
    # 父类dispatch方法:
        def dispatch(self, request, *args, **kwargs):
            # 请求方法小写如果在当前对象的http_method_names中(八个请求方法)
            '''
            http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
            '''
            if request.method.lower() in self.http_method_names:
                # 如果成立,那么执行下面的反射,从当前对象(视图类的对象)拿到请求方法,如果是get请求就拿到get方法,post请求就拿到post方法,然后赋给handler,handler就是相应请求对应的方法,最后返回handler(request),本质其实是get(request)
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                # 不成立,也就是没有该请求对应的方法,执行http_method_not_allowed方法,弹出警告
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
    
     
    
    
    # http_method_not_allowed源码
    def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return HttpResponseNotAllowed(self._allowed_methods())
    
    

DRF之APIView和Request对象分析

Django View和DRF APIView的小插曲

  • DRF中最“牛逼”的视图类是APIView,不过也继承了View

  • 导入:from rest_framework.views import APIView

  • Django中最“牛逼”的视图类是View

  • 导入:

    from django.views import View # 最简
    from django.views.generic import View
    from django.views.generic.base import View  # 最完整
    
    '''这三个导入的结果都是一个View,想知道缘由还得看源码和目录结构,如下:'''
    # 目录结构
    -django
    -···  # 此处省略n个包
    -views
    -- __init__.py
    -- generic
    ---- __init__.py
    ---- base.py
    # 源码分析
    '''问题:为什么上述不同的py文件都可以指向同一个类?'''
    我的思路是羊毛出在羊身上,既然都指向类,那么从结果出发是不是更好
    1、View类在base.py中,那么显而易见最完整的导入是没问题的,重点是为什么简写也可以
    2、base.py所在的包generic,如果从包中导入py文件我们知道可以通过“白名单”__all__来指定谁可以被其他文件导入,我们发现generic包中指定了:
    from django.views.generic.base import RedirectView, TemplateView, View和__all__ = [ 'View',·····],到此第二个导入解决
    # 那么第一个这么短这么简单又是为啥?
    3、同样在views包的__init__.py文件中发现:
    from django.views.generic.base import View
    __all__ = ['View']
    View类被__all__变量包裹了
    

APIView的执行流程

# 同样和Django中一样写一个视图类,只不过DRF中用APIView底层还是View
'''views.py'''
from rest_framework.response import Response
class Test(APIView):
    def get(self, request):
        data = {'status': 200, 'msg': 'success'}
        return Response(data)

    def post(self, request):
        data = {'status': 200, 'msg': 'success'}
        return Response(data)
'''urls.py'''
path('test/', views.Test.as_view())

APIView流程源码分析

1、路由:path('test/', views.Test.as_view()),path第二个参数任然返回函数内存地址,也还是类绑定方法,Test没有as_view方法,去继承的APIView中找,这次不需要和Django一样去View中找了,庆幸的是APIView中有as_view方法,核心源码如下:

@classmethod
    def as_view(cls, **initkwargs):
        # 校验反射的结果
         if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '····
                )
           ···
        
        # 核心代码
        # 这里调用了父类的as_view,查看源码发现又回到了Django中的View类,所以本质还是和上面一样,用到了闭包返回的view
        view = super().as_view(**initkwargs)
		···
        # 局部去掉了csrf校验和加装饰器的效果是一样的
        return csrf_exempt(view)

2、view = super().as_view(**initkwargs),这里跳转了一下,其实看了父类(View)的源码是和上面Django中写视图类继承的View是一样的,这里的(APIView)的as_view只是进行了简单处理和去掉了csrf中间件校验,真实使用的还是View类中的as_view
3、然后还是闭包函数的返回值view加括号调用,传入了当前对象的request,继续执行了self.dicpatch(),当前类(Test)没有去父类(APIview)找dispatch方法,发现APIView类中有,千万注意了这里可不是View中的dispatch方法了
4、APIView类中的dispatch主要源码:
# APIView的dispatch
  def dispatch(self, request, *args, **kwargs):
    	# request是新的drf提供的request,它是由老的django的request得到的
      	# 通过老request,生成一个新request,drf的Request的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 把新的request,放到了视图类对象中,可以通过self调用新的request和传入的request是一个,因为放到了self中
        self.request = request
        try:
            # 执行了三大认证(认证,权限,频率)
            self.initial(request, *args, **kwargs)
            # self.http_method_names是个列表
            if request.method.lower() in self.http_method_names:
              	# 原来dispatch的核心代码
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            # 原来dispatch写的,但是request已经不是老request了,是新的
            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

总结:
1、路由:path('test/', views.Test.as_view())
2、APIView类的as_view执行,最终使用View类的as_view
3、执行闭包返回view加括号调用到此就是as_view加括号调用
4、调用执行了view()返回dispatch,但是这里的父类不是View,是APIview所以执行的dispatch是APIView中的dispatch方法
5、dispatch方法中包装了新的Request对象,以后视图类中的方法传入的request都是新的,无论三大认证还是视图函数的方法,执行过程中出了异常,都会被处理掉
6、dispatch执行完毕返回reponse对象,跳转回进入视图函数继续执行as_view去掉了csrf校验

如何包装了新的request?

# APIView中的dispatch方法处理老的request
request = self.initialize_request(request, *args, **kwargs)

# initialize_request方法,当前类(自己写的)没有去父类找,还是APIView
 def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request, # 老的request,传入的非处理的,返回后就是新的了
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
注意:上面返回的Request对象是rest_framework导入的,然后实例化后返回的,实例化就少不了__init__构造函数
from rest_framework.request import Request
'''Request类'''
class Request:
    # 这里即将要初始化的request是传入的老的request
     def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
     ··· 
     # 初始化,将传入的老的request放入新的_request中,所以request._request是老的,self.request._request等价于request._request
	    self._request = request
      ···
    
# 验证一下新老request
class Test(APIView):
    def get(self, request):
        data = {'status': 200, 'msg': 'success'}
        # 新的request
        print(type(request)) # <class 'rest_framework.request.Request'>
        print(type(self.request))# <class 'rest_framework.request.Request'>
        # 老的request
        print(type(self.request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
        print(type(request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
        return Response(data)
    
新的:<class 'rest_framework.request.Request'>
老的:<class 'django.core.handlers.wsgi.WSGIRequest'>

三大认证如何执行?

# APIView类处理三大认证
self.initial(request, *args, **kwargs)
# 核心源码
def initial(self, request, *args, **kwargs):
    ····
    self.perform_authentication(request) # 认证
    self.check_permissions(request) # 权限
    self.check_throttles(request) # 频率
    ···
# 经过了三大认证才进入了视图函数
中间件---路由---···---三大认证---视图函数····
# 类似二次校验

Request对象分析

rest_framework.request.Request常用属性和方法
这里的request和原来的Django使用request一样,只是多了一个request.data

  • request.data:前端POST提交的数据,可以处理多种格式的数据,无论前端传什么编码post提交的数据都在data中

    ps:原来提交的数据在request.POST里,有局限性只能处理urlencoded和formdata编码格式,json格式不能处理,是没有request.data的,request其余使用方法的都一样

    # 如果用过新包装过的request来调用原来的方法呢?这样给使用者的感觉确实没什么变化源码如下:
    '''重写了getattr'''
        def __getattr__(self, attr):
            try:
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)
    
    重写getattr的结果是,新的request.method执行的时候本质是request._request.method执行了原来老的request的方法,通过这句getattr(self._request, attr)反射,所以才一样
    
    
    总结:新的request当老的用即可,只是多了个data前端post请求传入的数据,三种编码格式都可以获取

验证处理三种编码格式

  • json格式,只有request.data可以获取,结果是字典
  • form-data格式和urlencode格式都可以获取并且是QueryDict对象
    from rest_framework.response import Response
    class Test(APIView):
        def post(self, request):
            data = {'status': 200, 'msg': 'success'}
            print(request.POST)
            print(request._request.POST)
            print(request.data)
            return Response(data)
    
    
    # form-data格式结果
    <QueryDict: {'name': ['alan']}>
    <QueryDict: {'name': ['alan']}>
    <QueryDict: {'name': ['alan']}>
    # urlencode格式结果
    <QueryDict: {'name': ['alan']}>
    <QueryDict: {'name': ['alan']}>
    <QueryDict: {'name': ['alan']}>
    # json格式结果
    <QueryDict: {}>
    <QueryDict: {}>
    {'name': 'alan'}
    

ps:如果想查看QueryDict的源码怎么看?

1、type这个对象找到他属于哪个类
2、导入该类,点进去查看
'''demo'''
print(type(request.POST)) # 老的POST
from django.http.request import QueryDict

注意:如果前端提交过来多个同名数据也就是form表单中input标签的name属性设置了多个重名数据就不能使用request.POST.get('user')来获取了,这样只能获取到一个,需要使用getlist来获取,取出全部

image

原来的django中没有request.data,造一个!

# 原来的django的request对象中没有data,使得request.data-->无论什么编码格式,post提交数据,data都有值
from django.views import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from functools import wraps
def outter(func):
    @wraps(func)
    def inner(request,*args,**kwargs):
        import json
        if request.POST:
            request.data = request.POST
        else:
            # 将request.body从json对象转换为字典
            request.data = json.loads(request.body)
        res = func(request,*args,**kwargs)
        return res
    return inner

@method_decorator(outter,name='post')
class TestView(View):
    def get(self, request):
        return HttpResponse('get请求')

    
    def post(self, request):
   		# 只测试了json格式其余都可以也测了
        print(request.data) # {'name': 'Hammer'}
        print(request.POST)  # <QueryDict: {}>
        return HttpResponse('post请求')

序列化器serializer

什么是序列化和反序列化?

  • 序列化:序列化器会把模型对象(QuerySet对象,比如book)转换成字典,经过response以后变成了json字符串
  • 反序列化:将客户端(前端)发送过来的数据,经过request以后变成字典(data),序列化器可以把字典转换成模型存到数据库中
  • 存数据库需要校验,反序列化就可以帮我们完成数据的校验功能

序列化

  • 序列化demo
    • 在app中新建serializer.py,自定义类,继承DRF框架的Serializer及其子类
    • 在类中写要序列化的字段(序列化哪些就写哪些,不序列化的不写)
    • 使用序列化类,视图类中用,得到序列化类对象,对象.data,通过Response返回给前端
  • serializer.py:序列化类
    from rest_framework import serializers
    
    
    # 继承Serializer
    class BookSerializer(serializers.Serializer):
        '''
        max_length=32
        min_length=3  反序列化保存校验数据的时候用,序列化不用
        '''
        # 写要序列化的字段
        title = serializers.CharField()
        #  models中使用了DecimalField,这个位置使用了CharField会把小数类型转成字符串,使用CharField或者DecimalField都可以
        # 这里不需要担心反序列化存的问题
        price = serializers.CharField()
        authors = serializers.CharField()
    
    
  • views.py:视图类
    from rest_framework.views import APIView
    from .models import Book
    from .serializer import BookSerializer
    from rest_framework.response import Response
    
    
    # Create your views here.
    class BookView(APIView):
        def get(self, request):
            # 从数据库查数据,做序列化
            book_list = Book.objects.all()
            # 实例化类,传入初始化的参数,instance和many
            '''
            instance:要序列化的对象  qs,单个对象
            many:如果是qs对象,many=True,如果是单个对象many=False
            '''
            ser = BookSerializer(instance=book_list, many=True)
            # ser.data使用模型类的对象得到序列化后的字典
            return Response(ser.data)
    
  • urls.py:路由
    path('books/', views.BookView.as_view()),
    
  • model.py:模型类
    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=5,decimal_places=2)
        authors = models.CharField(max_length=32)
    
    image
  • 注意
    • 视图类中的参数instance和many的使用,instance是要序列化的对象,一般从数据库中获取到的,many=True代表要序列化多个对象,如果是单个对象就等于False
    • 序列化器中不要写max_length等参数,反序列化验证字段用
    • 在对BookSerializer类实例化传入的参数不知道传什么,由于我们没有写构造函数,去父类看需要什么参数传什么就可以了
    • 使用浏览器测得时候一定要注册rest_framework

字段类型

  • serializer.py文件中常用的字段
    字段 字段构造方式
    BooleanField BooleanField()
    NullBooleanField NullBooleanField()
    CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
    RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLField URLField(max_length=200, min_length=None, allow_blank=False)
    UUIDField UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
    IntegerField IntegerField(max_value=None, min_value=None)
    FloatField FloatField(max_value=None, min_value=None)
    DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
    DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationField DurationField()
    ChoiceField ChoiceField(choices) choices与Django的用法相同
    MultipleChoiceField MultipleChoiceField(choices)
    FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListField ListField(child=, min_length=None, max_length=None)
    DictField DictField(child=)
  • 常用的有:
    CharField
    IntegerField
    FloatField
    DecimalField
    DateTimeField
    DateField
    
    注意
    '''
    ListField和DictField原来的models是没有的,主要用来做反序列,处理前端传入的数据
    '''
    比如我们从前端接收json格式数据
    "hobby":["篮球","足球"] 可以用ListField处理
    "wife":{"name":"ll","age":20}  DictField类似使用
    

字段参数

写在类中的参数

  • 选项参数:
    参数名称 作用
    max_length 最大长度(CharField)
    min_lenght 最小长度(CharField)
    allow_blank 是否允许为空(CharField)
    trim_whitespace 是否截断空白字符(CharField)
    max_value 最小值 (IntegerField)
    min_value 最大值(IntegerField)
  • 通用参数:
    参数名称 说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器(不太用)
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息
  • 总结:
    # 重点记忆:
    read_only:表示该字段仅用于序列化输出,默认为Fasle,如果read_only = True,这个字段只用来做序列化(对象---json---前端)
    write_only:表明该字段仅用于反序列化输入,默认False,如果write_only = True,那么这个字段只用来做反序列化(前端---json---存数据库)
    # 注意
    如果不写read_only和write_only表示及序列化又反序列化
    千万不要同时写read_only=True和write_only=True逻辑矛盾了,都要实现直接省略即可
    
  • demo
    title=serializers.CharField(max_length=32,min_length=3)
    price=serializers.CharField(write_only=True,)
    author=serializers.CharField(write_only=True)
    
    # 上面title字段及序列化也反序列化,price,author字段只反序列化
    # 序列化给前端,前端看到的字段样子---》只能看到name
    # 反序列化,前端需要传什么过name,price,author都传
    
    image

序列化自定制返回字段

  • 方法一:在序列化类(serializers.py)中写

    自定义返回的key或者value

    '''serializer.py'''
    from rest_framework import serializers
    
    # 继承Serializer
    class BookSerializer(serializers.Serializer):
        # 写要序列化的字段
        title = serializers.CharField(read_only=True)
        price = serializers.CharField(write_only=True)
        authors = serializers.CharField(write_only=True)
    
        # 自定制返回字段
        author_info = serializers.SerializerMethodField()
        # 搭配方法,方法名必须是get_字段名,该方法返回什么字段,显示什么
        def get_author_info(self,obj):
            # obj是当前数据库book对象
            return obj.authors+'牛掰'
            # 注意字符串拼接的问题
    
        price = serializers.SerializerMethodField()
        def get_price(self,obj):
            return "价格是:"+str(obj.price)
    
    image
  • 方法二:在表模型(models.py)中写

    '''models.py'''
    from django.db import models
    
    # Create your models here.
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=5,decimal_places=2)
        authors = models.CharField(max_length=32)
    
    
        @property
        def price_info(self):
            return '价格是:'+str(self.price)
    
    '''serializer.py'''
    from rest_framework import serializers
    
    # 继承Serializer
    class BookSerializer(serializers.Serializer):
        # 写要序列化的字段
        title = serializers.CharField(read_only=True)
        price = serializers.CharField(write_only=True)
        authors = serializers.CharField(write_only=True)
        
        # 该字段是从models的price_info返回值获取到的,price_info方法返回什么,这个字段就是什么
        price_info = serializers.CharField()
    
    • ListField的应用
      '''models.py'''
      def authors(self):
              return [{"name":"Hammer","age":18},{"name":"Hans","age":28}]
      '''serializer.py'''  
      authors = serializers.ListField()
      
      image
      image

反序列化

  • 把前端传入的数据,放到Serializer对象中:ser=BookSerializer(data=request.data)

  • 校验数据:ser.is_valid()

  • 保存,ser.save(),但是必须重写create,在序列化类中

  • 反序列化新增

    POST请求处理新增

    '''views.py'''
    from rest_framework.views import APIView
    from .models import Book
    from rest_framework.response import Response
    from app01.serializer import  BookSerializer
    
    class BookView(APIView):
        
        def post(self,request):
            # 反序列化,保存到数据库使用data参数
            deser = BookSerializer(data=request.data)
            # 校验数据
            if deser.is_valid():
                # 保存需要重写create方法,不然不知道存到哪个表
                deser.save()
                return Response(deser.data)
            return Response({'code':101,'msg':'校验不通过','errors':deser.errors})
    

    重写create方法

    '''serializer.py'''
    def create(self, validated_data):
        # validated_data是校验通过的数据,将校验通过的数据打散存入数据库
        book = Book.objects.create(**validated_data)
        return book
    

    image

  • 反序列化修改

    # 处理修改再写一个视图类,防止get冲突
    class BookDetailView(APIView):
        # 获取一条的
        def get(self,request,pk):
            book = Book.objects.filter(pk=pk).first()
            ser = BookSerializer(instance=book)  # 这里设置了主键值,单条记录many不需要写
            return Response(ser.data)
        # 删除一条的
        def delete(self,request,pk):
            res = Book.objects.filter(pk=pk).delete()
            print(res) # (1, {'app01.Book': 1})
            # res是影响的行数
            if res[0]>0:
                return Response({'code': 100, 'msg': '删除成功'})
            else:
                return  Response({'code': 103, 'msg': '要删除的数据不存在'})
    
        # 反序列化修改
        def put(self,request,pk):
            # 修改处理单条数据用过pk确定求改哪条数据
            book = Book.objects.filter(pk=pk).first()
            # 序列化器类实例化需要传入instance,data才表示修改
            ser = BookSerializer(instance=book,data=request.data)
            if ser.is_valid():
                # 重写update方法才能存入
                ser.save()
                return Response(ser.data)
            return Response({'code':101,'msg':'校验未通过','error':ser.errors})
    

    重写update方法

    '''serializer.py'''   
    def update(self, instance, validated_data):
        '''
        :param instance: 表示要修改的对象
        :param validated_data: 校验通过的数据
        :return: instance
        '''
        # 如果只修改一个的情况,从校验通过的数据中get到其他数据是none,做一层校验
        instance.title = validated_data.get('title')
        instance.price = validated_data.get('price')
        instance.authors = validated_data.get('authors')
        instance.save()  # 保存到数据库中
        return instance  # 返回instance对象
    

    路由

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', views.BookView.as_view()),
        path('books/<int:pk>', views.BookDetailView.as_view()),
    ]
    
  • 反序列化之局部和全局钩子

     '''serializer.py'''
    
    # 局部钩子
    def validate_title(self,attr):
        # attr就是前端传入的数据
        # 局部校验书名
        if attr.startswith('sb'):
            from django.core.exceptions import ValidationError
            raise ValidationError("名字不能以sb开头")
        else:
            return attr  # 没有问题,正常返回
    '''
    校验顺序:先走字段自己规则,再走局部钩子,再走全局钩子
    '''
    # 全局钩子
    def validate(self,attrs):
        # attrs校验过后的数据,通过了前面校验的规则
        if attrs.get('title') == attrs.get('authors'):
            from django.core.exceptions import ValidationError
            raise ValidationError('作者名不能等于书名')
        else:
            return attrs
    

    image
    image

ModelSerializer模型类序列化器

ps:视图类,路由处理方式和Serializer是一样的

# ModelSerializer和表模型有绑定关系
class BookSerializer1(serializers.ModelSerializer):
    class Meta:
        model = Book  # 指定和哪个表有关系
        # 所有字段
        # fields = '__all__'
        # 这里注意id字段是从表模型映射过来的,auto自增的,不传也可以
        # 自定制的字段不传必须注册,在列表中
        fields = ['id', 'title', 'price', 'price_info']  # 指定字段
        extra_kwargs = {
            'title': {'write_only': True, 'max_length': 8, 'min_length': 3}
        }
    # 指定序列化的字段:两种写法:在序列化类中写;models中写
    price_info = serializers.SerializerMethodField()
    def get_price_info(self, obj):
        return "价格是:" + str(obj.price)
    '''
    注意:自定制字段如果和表模型获取到的字段是同名,那么自定制返回给前端的字段值就被自定制覆盖了,比如:
    title = serializers.SerializerMethodField()
    def get_title(self, obj):
        return "书名是:" + str(obj.title)
    '''

    #  局部和全局钩子,跟之前一样,但是要注意写在Meta外
  • ModelSerializer类序列化器不需要重写create方法和update方法了,因为明确指定了操作哪个表
  • 固定写法,ModelSerializer类内写Meta类,用来指定一些字段和表模型

ModelSerializer序列化器实战

单表操作

  • 序列化器类

    # ModelSerializer和表模型有绑定关系
    
    class BookSerializer1(serializers.ModelSerializer):
    
        class Meta:
    
            model = Book  # 指定和哪个表有关系
    
            # 所有字段
    
            # fields = '__all__'
    
            # 这里注意id字段是从表模型映射过来的,auto自增的,不传也可以
    
            # 自定制的字段不传必须注册,在列表中
    
            fields = ['id', 'title', 'price', 'price_info']  # 指定字段
    
            extra_kwargs = {
    
                'title': {'write_only': True, 'max_length': 8, 'min_length': 3}
    
            }
    
        # 指定序列化的字段:两种写法:在序列化类中写;models中写
    
        price_info = serializers.SerializerMethodField()
    
        def get_price_info(self, obj):
    
            return "价格是:" + str(obj.price)
    
        '''
    
        注意:自定制字段如果和表模型获取到的字段是同名,那么自定制返回给前端的字段值就被自定制覆盖了,比如:
    
        title = serializers.SerializerMethodField()
    
        def get_title(self, obj):
    
            return "书名是:" + str(obj.title)
    
        '''
    
    
    
        #  局部和全局钩子,跟之前一样,但是要注意写在Meta外
    
  • 视图类

    from rest_framework.views import APIView
    
    from .models import Book
    
    from rest_framework.response import Response
    
    from app01.serializer import  BookSerializer1
    
    class BookView1(APIView):
    
        def get(self, request):
    
            # 从数据库查数据,做序列化
    
            book_list = Book.objects.all()
    
            # 实例化类,传入初始化的参数,instance和many
    
            '''
    
            instance:要序列化的对象  qs,单个对象
    
            many:如果是qs对象,many=True,如果是单个对象many=False
    
            '''
    
            ser = BookSerializer1(instance=book_list, many=True)
    
            # ser.data使用模型类的对象得到序列化后的字典
    
            return Response(ser.data)
    
    
    
        def post(self,request):
    
            # 反序列化,保存到数据库使用data参数
    
            deser = BookSerializer1(data=request.data)
    
            # 校验数据
    
            if deser.is_valid():
    
                # 保存需要重写create方法,不然不知道存到哪个表
    
                deser.save()
    
                return Response(deser.data)
    
            return Response({'code':101,'msg':'校验不通过','errors':deser.errors})
    
    
    
    
    
    
    
    # 处理修改再写一个视图类,防止get冲突
    
    class BookDetailView1(APIView):
    
        def get(self,request,pk):
    
            book = Book.objects.filter(pk=pk).first()
    
            ser = BookSerializer1(instance=book)  # 这里设置了主键值,单条记录many不需要写
    
            return Response(ser.data)
    
        def delete(self,request,pk):
    
            res = Book.objects.filter(pk=pk).delete()
    
            print(res) # (1, {'app01.Book': 1})
    
            # res是影响的行数
    
            if res[0]>0:
    
                return Response({'code': 100, 'msg': '删除成功'})
    
            else:
    
                return  Response({'code': 103, 'msg': '要删除的数据不存在'})
    
    
    
        # 反序列化修改
    
        def put(self,request,pk):
    
            # 修改处理单条数据用过pk确定求改哪条数据
    
            book = Book.objects.filter(pk=pk).first()
    
            # 序列化器类实例化需要传入instance,data才表示修改
    
            ser = BookSerializer1(instance=book,data=request.data)
    
            if ser.is_valid():
    
                # 重写update方法才能存入
    
                ser.save()
    
                return Response(ser.data)
    
            return Response({'code':101,'msg':'校验未通过','error':ser.errors})
    
  • 路由

    path('books1/', views.BookView1.as_view()),
    
    path('books1/<int:pk>', views.BookDetailView1.as_view()),
    
  • 模型

    from django.db import models
    
    class Book(models.Model):
    
        title = models.CharField(max_length=32)
    
        price = models.DecimalField(max_digits=5,decimal_places=2)
    
        authors = models.CharField(max_length=32)
    
    

多表操作

  • models.py

    from django.db import models
    
    
    # build four model tables
    
    
    class Book(models.Model):
        name = models.CharField(max_length=32)
        price = models.DecimalField(decimal_places=2, max_digits=5)
        publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
        authors = models.ManyToManyField(to='Author')
    
        def __str__(self):
            return self.name
    
        # 自定制字段
    
        @property
        def publish_detail(self):
            return {'name': self.publish.name, 'addr': self.publish.city}
    
        @property
        def author_list(self):
            l = []
            print(self.authors.all())  # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
    
            for author in self.authors.all():
                print(author.author_detail)  # AuthorDetail object (1)
                l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr})
            return l
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
        author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    
        def __str__(self):
            return self.name
    
        # @property
        # def authordetail_info(self):
        #     return {'phone': self.author_detail.telephone, 'addr': self.author_detail.addr}
    
    
    class AuthorDetail(models.Model):
        telephone = models.BigIntegerField()
        addr = models.CharField(max_length=64)
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        city = models.CharField(max_length=32)
        email = models.EmailField()
    
  • serializer.py

    from src.index import models
    
    from rest_framework import serializers
    
    
    # 书序列化器
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            # 指定和哪个表有关系
            model = models.Book
            # fields = '__all__'
            fields = ['id', 'name', 'publish', 'price', 'authors', 'publish_detail', 'author_list']
            # 将关联表的信息全部取出来,不推荐使用
            # depth = 1
    
            extra_kwargs = {
                'publish': {'write_only': True},
                'authors': {'write_only': True},
            }
    
    
    # 作者序列化器
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            # 指定和哪个表有关系
            model = models.Author
            fields = '__all__'
            # fields = ['id', 'name', 'age', 'author_detail', 'authordetail_info']
    
            extra_kwargs = {
                'author_detail': {'write_only': True},
            }
    
    
    # 作者详情序列化器
    class AuthorDetailSerializer(serializers.ModelSerializer):
        class Meta:
            # 指定和哪个表有关系
            model = models.AuthorDetail
            fields = '__all__'
    
    
    # 出版社序列化器
    class PublishSerializer(serializers.ModelSerializer):
        class Meta:
            # 指定和哪个表有关系
            model = models.Publish
            fields = '__all__'
    
  • views.py

    from django.shortcuts import render
    
    from src.index import models
    from src.index import serializer
    from rest_framework.response import Response
    from rest_framework.views import APIView
    
    
    def base(request):
        return render(request, 'base.html')
    
    
    # 书视图类
    class BookView(APIView):
    
        # 获取所有
        def get(self, requets):
            # 序列化
            book_list = models.Book.objects.all()
            # 序列化多条数据many=True
            ser = serializer.BookSerializer(instance=book_list, many=True)
            return Response(ser.data)
    
        # 新增一条
        def post(self, request):
            # 获取反序列化数据
            ser = serializer.BookSerializer(data=request.data)
            if ser.is_valid():
                # 校验通过存入数据库,不需要重写create方法了
                ser.save()
                return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
            # 校验失败
            return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
    
    
    class BookViewDetail(APIView):
    
        def get(self, request, pk):
            book = models.Book.objects.filter(pk=pk).first()
            ser = serializer.BookSerializer(instance=book)
            return Response(ser.data)
    
        def put(self, request, pk):
            book = models.Book.objects.filter(pk=pk).first()
            # 修改,instance和data都要传
            ser = serializer.BookSerializer(instance=book, data=request.data)
            if ser.is_valid():
                # 校验通过修改,不需要重写update
                ser.save()
                return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
            # 校验不通过
            return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
    
        def delete(self, request, pk):
            models.Book.objects.filter(pk=pk).delete()
            return Response({'code': 100, 'msg': '删除成功'})
    
    
    # 作者视图类
    class AuthorView(APIView):
        def get(self, requets):
            # 序列化
            author_list = models.Author.objects.all()
            # 序列化多条数据many=True
            ser = serializer.AuthorSerializer(instance=author_list, many=True)
            return Response(ser.data)
    
        def post(self, request):
            # 获取反序列化数据
            ser = serializer.AuthorSerializer(data=request.data)
            if ser.is_valid():
                # 校验通过存入数据库,不需要重写create方法了
                ser.save()
                return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
            # 校验失败
            return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
    
    
    class AuthorViewDetail(APIView):
    
        def get(self, request, pk):
            book = models.Author.objects.filter(pk=pk).first()
            ser = serializer.AuthorSerializer(instance=book)
            return Response(ser.data)
    
        def put(self, request, pk):
            book = models.Author.objects.filter(pk=pk).first()
            # 修改,instance和data都要传
            ser = serializer.AuthorSerializer(instance=book, data=request.data)
            if ser.is_valid():
                # 校验通过修改,不需要重写update
                ser.save()
                return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
            # 校验不通过
            return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
    
        def delete(self, request, pk):
            models.Author.objects.filter(pk=pk).delete()
            return Response({'code': 100, 'msg': '删除成功'})
    
    
    # 作者详情视图类
    
    class AuthorDetailView(APIView):
    
        def get(self, requets):
            # 序列化
            author_list = models.AuthorDetail.objects.all()
            # 序列化多条数据many=True
            ser = serializer.AuthorDetailSerializer(instance=author_list, many=True)
            return Response(ser.data)
    
        def post(self, request):
            # 获取反序列化数据
            ser = serializer.AuthorDetailSerializer(data=request.data)
            if ser.is_valid():
                # 校验通过存入数据库,不需要重写create方法了
                ser.save()
                return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
            # 校验失败
            return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
    
    
    class OneAuthorViewDetail(APIView):
    
        def get(self, request, pk):
            book = models.AuthorDetail.objects.filter(pk=pk).first()
            ser = serializer.AuthorDetailSerializer(instance=book)
            return Response(ser.data)
    
        def put(self, request, pk):
            book = models.AuthorDetail.objects.filter(pk=pk).first()
            # 修改,instance和data都要传
            ser = serializer.AuthorDetailSerializer(instance=book, data=request.data)
            if ser.is_valid():
                # 校验通过修改,不需要重写update
                ser.save()
                return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
            # 校验不通过
            return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
    
        def delete(self, request, pk):
            models.AuthorDetail.objects.filter(pk=pk).delete()
            return Response({'code': 100, 'msg': '删除成功'})
    
    
    # 出版社视图类
    
    class PublishView(APIView):
    
        def get(self, requets):
            # 序列化
            author_list = models.Publish.objects.all()
            # 序列化多条数据many=True
            ser = serializer.PublishSerializer(instance=author_list, many=True)
            return Response(ser.data)
    
        def post(self, request):
            # 获取反序列化数据
            ser = serializer.PublishSerializer(data=request.data)
            if ser.is_valid():
                # 校验通过存入数据库,不需要重写create方法了
                ser.save()
                return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
            # 校验失败
            return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
    
    
    class PublishViewDetail(APIView):
    
        def get(self, request, pk):
            book = models.Publish.objects.filter(pk=pk).first()
            ser = serializer.PublishSerializer(instance=book)
            return Response(ser.data)
    
        def put(self, request, pk):
            book = models.Publish.objects.filter(pk=pk).first()
            # 修改,instance和data都要传
            ser = serializer.PublishSerializer(instance=book, data=request.data)
            if ser.is_valid():
                # 校验通过修改,不需要重写update
                ser.save()
                return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
    
            # 校验不通过
            return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
    
        def delete(self, request, pk):
            models.Publish.objects.filter(pk=pk).delete()
            return Response({'code': 100, 'msg': '删除成功'})
    
  • urls.py

    # 二级路由,路由分发
    from django.urls import path
    from src.index import views
    from rest_framework.routers import SimpleRouter
    
    # router = SimpleRouter()
    # router.register('books', views.BookView)
    urlpatterns = [
    
        # 书
        path('books/', views.BookView.as_view()),
        path('books/<int:pk>', views.BookViewDetail.as_view()),
    
        # 作者
        path('authors/', views.AuthorView.as_view()),
        path('authors/<int:pk>', views.AuthorViewDetail.as_view()),
    
        # 作者详情
        path('authorsdetail/', views.AuthorDetailView.as_view()),
        path('authorsdetail/<int:pk>', views.OneAuthorViewDetail.as_view()),
    
        # 出版社
        path('publish/', views.PublishView.as_view()),
        path('publishdetail/<int:pk>', views.PublishViewDetail.as_view()),
    
    ]
    # urlpatterns += router.urls
    
  • 优化操作

    我们知道作者表和作者详情表的表关系是一对一的关系,那么新增数据的时候,就得先新增作者详情表,再增作者表的数据,但是在实际生活中,用户不知道表关系这码事,为了体验更好,可以重写create方法,同时存两个表的内容,给用户的感觉就是操作了一张表

    # 优化作者表的序列化器
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            # 指定和哪个表有关系
            model = models.Author
            # fields = '__all__'
            fields = ['id', 'name', 'age', 'telephone', 'addr', 'authordetail_info']
    
        # 重写字段telephone和addr
        telephone = serializers.CharField(write_only=True)
        addr = serializers.CharField(write_only=True, max_length=8, required=False)
    
        # 重写create,操作两个表
        def create(self, validated_data):
            # 先存作者详情
            authordetail = models.AuthorDetail.objects.create(telephone=validated_data.get('telephone'),
                                                              addr=validated_data.get('addr'))
            # 存作者表
            author = models.Author.objects.create(author_detail=authordetail, name=validated_data.get('name'),
                                                  age=validated_data.get('age'))
            # 这样只返回author对象就行,直接存了两个表,返回反序列化的对象
            return author
    

admin后台使用

  • 建造超级用户

    manage.py文件下createsuperuser命令

    • 输入账户密码即可
  • admin.py配置

    from django.contrib import admin
    from .models import *
    # Register your models here.
    
    
    admin.site.register(Book)
    admin.site.register(Author)
    admin.site.register(AuthorDetail)
    admin.site.register(Publish)
    
  • 语言时间国际化

    """settings.py"""
    
    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_TZ = False
    
posted @ 2022-10-27 00:04  爱learn  阅读(69)  评论(0编辑  收藏  举报