django-rest-framework框架(组件,序列化)

django-rest-framework

知识点:

1、接口:什么是接口、restful接口规范
2、CBV生命周期源码 - 基于restful规范下的CBV接口
3、请求组件、解析组件、响应组件
4、序列化组件(灵魂)
5、三大认证(重中之重):认证、权限(权限六表)、频率
6、其他组件:过滤、筛选、排序、分页、路由

1、接口:

接口:联系两个物质的媒介,完成信息交互
在web程序中,联系前台页面与后台数据库的媒介

web接口组成:
url:长得像放回数据的URL链接
请求参数:请求按照指定的Key提供数据给后台
响应数据:后台与数据库交互后将数据反馈给前台

restful接口规范

接口规范:就是为了采用不同的后台语言,也能使用同样的接口获取同样的数据
如何学习:接口规范是 规范化书写接口的,写接口要写url、响应数据
注意:如果将请求参数也纳入考量范围,那就是在写 接口文档

两大部分:

  • url
1) 用api关键字标识接口url
	api.baidu.com | www.baidu.com/api
	
2) 接口数据安全性决定优先选择https协议

3)如果一个接口有多版本存在,需要在url中标识体现
	api.baidu.com/v1/... | api.baidu.com/v2/...

4)接口操作的数据源称之为资源,在url中一般采用资源复数形式,一个接口可以概括对该资源的多种操作方式
	api.baidu.com/books | api.baidu.com/books/(pk)

5) 请求方式有多种,用一个url处理如何保证不混乱 - 通过请求方式表示操作资源的方式
	/books			get   	 		获取所有
	/books   		post   	 		增加一个(多个)
	/books/(pk)     delete	    	删除一个
	/books/(pk)     put  		整体更新一个
	/books/(pk)     patch           局部更新一个

6)资源往往涉及数据的各种操作方式 - 筛选、排序、限制
	api.baidu.com/books/?search=西&ordering=-price&limit=3 
  • 响应数据
1)httpq请求的响应会有响应状态码,接口用来返回操作的资源数据,可以拥有操作数据结果的状态码
	status	0(操作资源成功)	1(操作资源失败)	2(操作资源成功,但没匹配结果)
	注:资源状态码不像http状态码,一般都是前后端相约定的。

2)资源的状态码文字提示
	status	ok	‘账号有误’	‘密码有误’	‘用户锁定’

3)资源本身
	results
	注:删除资源成功不做任何数据返回(返回空字符串)
4)不能直接放回的资源(子资源、图片、视频等资源),返回该资源的url链接

示例准备:

https://api.baidu.com/vi/books?limit=3
	get | post | delete | put | patch
	{
		'ststus':0,
		'msg':'ok',
		'result':[
			{
				'title':*,
				'img:https://*
			}
		]
	}

基于restful规范的原生Django接口

主路由:url.py

from django.contrib import admin
from django.urls import path,re_path
from django.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 路由分发
    re_path('api/',include('api.urls'))
]

api组件的子路由:url.py

from django.contrib import admin
from django.urls import path,re_path
from . import views
urlpatterns = [
    re_path(r'books/$', views.Book.as_view()),
    re_path(r'books/(?P<pk>.*)/$', views.Book.as_view()),
]

模型层:model.py

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    class Meta:
        db_table = 'my_book'
        verbose_name='书籍'
        verbose_name_plural = verbose_name #复数形式

    def __str__(self):
        return  '《%s》' % self.title

后台层:admin.py

from django.contrib import admin

from . import models

admin.site.register(models.Book)

接着进行数据库迁移:

python mange.py makemigrations
python manage.py migrate

python mange.py createsuperuser

视图层:views.py

from django.http import JsonResponse
from django.views import View
from . import models

# 六大基础接口: 获取一个 获取所有 增加一个 删除一个 更新一个 更新所有
# 十大接口: 群增 群删 整体改群改 局部改群改
class Book(View):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        if not pk: #群查
            # 操作数据库
            book_obj_list = models.Book.objects.all()
            # 序列化过程
            book_list = []
            for obj in book_obj_list:
                dic = {}
                dic['title'] = obj.title
                dic['price'] = obj.price
                book_list.append(dic)
                # 响应数据
            return JsonResponse({
                'status': 0,
                'msg': 'ok',
                'results': book_list
            },json_dumps_params={'ensure_ascii': False})

        else:   #单查
            book_dic = models.Book.objects.filter(pk=pk).values('title','price').first()
            if book_dic:
                return JsonResponse({
                    'status': 0,
                    'msg': 'ok',
                    'results': book_dic
                },json_dumps_params={'ensure_ascii': False})
            return JsonResponse({
                'status': 2,
                'msg': 'no result',
            },json_dumps_params={'ensure_ascii': False})
                
            

        return JsonResponse('get ok', safe=False)
    # postman 可以完成不同方式的请求: get | post | put ... 
    # postman 发送数据包有三种方式: form-data | urlencoding |json
    # 原生django对urlencoding方式数据兼容最好
    def post(self,request,*args,**kwargs):

        # 前台通过urlencoding方式提交数据
        try:
            book_obj = models.Book.objects.create(**request.POST.dict())
            if book_obj:
                return JsonResponse({ 
                'status': 0,
                'msg': 'ok',
                'result': {'title': book_obj.title, 'price': book_obj.price},
            },json_dumps_params={'ensure_ascii': False})
        except:
            return JsonResponse({
                'status': 1,
                'msg': '参数有误!',
            },json_dumps_params={'ensure_ascii': False})
        return JsonResponse({
                'status': 2,
                'msg': '新增失败!',
            },json_dumps_params={'ensure_ascii': False})

#drf框架的封装风格
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.serializers import Serializer
from rest_framework.settings import APISettings
from rest_framework.filters import SearchFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import SimpleRateThrottle

# 总结:
# 1)drf 对原生request做了二次封装,request._request就是原生request
# 2)原生request对象的属性和方法都可以被drf的request对象直接访问(兼容)
# 2)drf请求的所有url拼接参数均被解析到query_params中,所有数据包数据都被解析到data中
class Test(APIView):
    def get(self, request, *args, **kwargs):
        # url拼接的参数
        print(request._request.GET) # 二次封装方式
        print(request.GET)  # 兼容
        print(request.query_params) # 拓展
        return Response('drf get ok')

    def post(self, request, *args, **kwargs):
        # 所有请求方式携带的数据包
        print(request._request.POST)  # 二次封装方式
        print(request.POST)  # 兼容
        print(request.data)  # 拓展,兼容性最强,三种数据都可以

        print(request.query_params)

        return Response('drf post ok')



# 在setting.py中配置REST_FRAMEWORK,完成的是局部配置,所有接口统一处理
# 如果只有部分接口特殊化,可以完成 —— 局部配置
from rest_framework.renderers import JSONRenderer
class Test2(APIView):
    renderer_classes = [JSONRenderer]
    def get(self, request, *args, **kwargs):
        return Response('drf get ok')

    def post(self, request, *args, **kwargs):
        return Response('drf post ok')


下载:
Postman接口工具

DRF框架:

安装:

pip install djangorestframework

接着,我们来分析下源码。

drf请求生命周期

1) 先请求执行APIView的as_view函数

2)在APIView的as_view调用父类(django原生)的as_view,还禁用了csrf认证

3)在父类的as_view中dispatch方法请求执行APIView的dispatch

4)完成任务方法交给视图类的请求函数处理,得到请求的响应结果,返回给前台

请求模块:request对象

源码入口:
APIView类的dispatch方法中:request = self.initialize_request(request, *args, **kwargs)

注:request完全兼容_request

渲染模块:浏览器和Postman请求结果渲染数据的方式不一样

源码入口:

#APIView类的dispatch方法
self.response = self.finalize_response(request, response, *args, **kwargs)


neg = self.perform_content_negotiation(request, force=True)

renderers = self.get_renderers()


REST_FRAMEWORK = {
    # drf提供的渲染类
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],

}

主要掌握:
response数据json与brower两种渲染方式
重点:全局配置/局部配置
self.render_classes
自定义视图类(局部)==>APIView视图类 =>自定义drf配置(全局)=>drf默认配置

解析模块:全局局部配置

源码入口:

#APIView类的dispatch方法
self.response = self.finalize_response(request, response, *args, **kwargs)

setting.py

REST_FRAMEWORK = {
    # 全局解析类配置
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.MultiPartParser'
    ],
}

views.py

class User(APIView):
	parser_classes = [JSONParser,JSONParser]
	def get():
		pass

异常模块

为什么要自定义异常模块

1)所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
2)drf默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
3)drf提供的处理方案两种,处理的返回异常,没处理返回None(后续就是服务器抛异常给前台)
4)自定义异常的目的就是解决drf没有处理的异常,让前台得到合理的信息返回,后台记录异常具体信息

源码分析

# 异常模块
response = self.handle_exception(exc)
# 获取处理异常的方法
# 一层层看源码,走的是配置文件,拿到的是rest_framework.views的exception_handler
# 自定义:直接写exception_handler函数,在自己的配置文件配置EXCEPTION_HANDLER指向自己
exception_handler = self.get_exception_handler()
# 异常处理的结果
# 自定义异常就是提供exception_handler异常处理函数,处理的目的就是让response一直有值
response = exception_handler(exc, context)

处理方案:自定义exception_handler函数如何书写实现体

# 修改自己的配置文件setting.py
# 自定义drf配置:

REST_FRAMEWORK = {
    # 全局配置异常模块
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
# 1) 先将异常处理交给rest_framework.views的exception_handler去处理
# 2)判断处理的结果(返回值)response,有值代表drf已经处理了,None代表需要自己处理

# 自定义异常处理文件exception.py,在文件中书写exception_handler函数
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.views import Response
from rest_framework import  status
def exception_handler(exc, context):
    # drf 的exception做基础处理
    response = drf_exception_handler(exc, context)
    # 为空,自定义二次处理
    if response is None:
        # print(exc)
        # print(context)

        print('%s - %s - %s' % (context['view'], context['request'].method, exc))
        return Response({
            'detail': '服务器错误',
        },status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
    return  response

响应模块

响应类构造器

def __init__(self, data=None, status=None,
	template_name=None, headers=None,
    exception=False, content_type=None):
        """
        	:param data: 响应数据
        	:param status: http响应状态码
        	:param template_name: drf也可以渲染页面,渲染的页面模板地址(不用)
        	:param headers: 响应头
        	:param exception: 是否异常了
        	:param content_type: 响应的数据格式(一般不用处理,响应头中带了,且默认是json)
        """
    pass             

常规实例化响应对象

from rest_framework import  status

return Response(data={数据}, status=status.HTTP_200_OK, header={设置的响应头})

序列化组件:

Serializer(偏底层)、ModelSeralizer(重点)、ListModelSerializer(辅助群改)

Serializer序列化模块

序列化准备:

模型层:models.py

class User(models.Model):
    SEX_CHOICES = [
        [0,'男'],
        [1,'女'],
    ]

    name = models.CharField(max_length=64)
    pwd = models.CharField(max_length=32)
    phone = models.CharField(max_length=11, null=True, default=None)
    sex = models.IntegerField(choices=SEX_CHOICES,default=0)
    icon = models.ImageField(upload_to='icon',default='icon/default.jpg')

    class Meta:
        db_table = 'user'
        verbose_name='用户'
        verbose_name_plural = verbose_name #复数形式

    def __str__(self):
        return  '%s' % self.name

后台管理层:admin.py

from django.contrib import admin
from . import models

admin.site.register(models.User)

配置:settings.py

INSTALLED_APPS = [
	#...
	'rest_framework',
	#...
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'api_test',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT':'3306'
    }
}

子路由:urls.py

from django.urls import path,re_path

from . import views

urlpatterns = [
    re_path(r'users/$', views.User.as_view()),
    re_path(r'users/(?P<pk>.*)/$', views.User.as_view()),
]

序列化使用:

  • 序列化:
步骤如下:
ser:
1)设置需要返回给前台,那些model类对应的字段,不需要返回的就不用设置了
2)设置方法字段,字段名可以随意,字段值有 get_字段名 提供,来完成一些需要处理再返回的数据

view:
1)从数据库中将要序列化给前台的model对象,或是多个model对象查询出来
2)将对象交给序列化处理,产生序列化对象,如果序列化的是多个数据,要设置many=True
3)序列化对象.data就是可以返回给前台的序列化数据

在app下创建api/serializer.py

# 序列化组件 - 为每一个model类通过一套序列化工具类
# 序列化组件的工作方式与django froms组件非常相似
from rest_framework import serializers,exceptions
from django.conf import settings
from . import models
class UserSerializer(serializers.Serializer):
    name = serializers.CharField()
    phone = serializers.CharField()
    # 序列化提供给前台的字段个数由后台决定,可以少提供,
    # 但是提供的数据库字段对应的字段,名字一定要与数据库字段相同
    #sex = serializers.IntegerField()
    #icon = serializers.ImageField()

    #自定义序列化属性
    #属性名随意,值由固定的命名规范方法提供,
    #       get_属性名(self,参与序列化的model对象)
    #       返回值就是自定义序列化属性的值
    gender = serializers.SerializerMethodField()
    def get_gender(self, obj):
        # choice类型的值解释性值 get_字段_display来访问
        return obj.get_sex_display()


    icon = serializers.SerializerMethodField()
    def get_icon(self, obj):
        # 自己配置的 /media/,给后面高级序列化与视图类准备的
        # obj.icon不能直接作为数据返回,因为内容虽然是字符串,但是类型是ImageFieldFile类型
        return '%s%s%s' % ('http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))

视图层:views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from . import models
from . import serializers


class User(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        print(pk)
        if pk:
            try:
                # 用户对象不能直接作为数据返回给前台
                user_obj = models.User.objects.get(pk=pk)
                # 序列化一下用户对象
                user_ser = serializers.UserSerializer(user_obj)
                print(user_ser,type(user_ser))
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_ser.data
                })
            except:
                return Response({
                    'status': 2,
                    'msg': '用户不存在',
                })
        else:
            # 用户对象列表(queryset)不能直接作为数据返回给前台
            user_obj_list = models.User.objects.all()
            # 序列化一下用户对象
            user_ser_data =serializers.UserSerializer(user_obj_list, many=True).data
            return Response({
                'status': 'ok',
                'msg': 0,
                'results':user_ser_data
            })
  • 反序列化:
    api/serializers.py
1) 设置必填与选填序列化字段,设置校验规则
2) 为需要额外校验的字段提供局部钩子参数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验
3) 为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验
4) 重写create方法,完成校验通过的数据库入库工作,得到新增的对象
class UserDeserializer(serializers.Serializer):
    # 1、哪些字段必须反序列化
    # 2、字段都有哪些安全校验
    # 3、哪些字段需要额外提供校验
    # 4、哪些字段间存在联合校验
    # 注:反序列化字段都是用来入库,不会出现自定义方法属性,会出现可以设置校验规则的自定义属性(re_pwd)
    name = serializers.CharField(
        max_length=64,
        min_length=3,
        error_messages={
            'max_length':'太长',
            'min_length':'太短'
        }
    )
    pwd = serializers.CharField()
    phone = serializers.CharField(required=False)
    sex = serializers.IntegerField(required=False)

    # 自定义有校验规则的反序列化字段
    re_pwd = serializers.CharField(required=True)

    # 局部钩子:validate_要校验的字段名(self,当前要校验的字段值)
    # 校验规则:校验通过返回原值,校验失败,抛出异常
    def validate_name(self,value):
        if 'g' in value.lower(): #名字中不能出现g
            raise exceptions.ValidationError('名字非法!')
        return value

    # 全局钩子:(固定写法)validate(self,系统与就不钩子校验通过的所有数据)
    def validate(self, attrs):
        pwd = attrs.get('pwd')
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise exceptions.ValidationError({'pwd&re_pwd':'两次密码不一致'})
        return attrs


    # 要完成新增,需要自己重写create()方法
    def create(self, validated_data):
    # 尽量在所有校验完毕之后,数据可以直接入库
        return models.User.objects.create(**validated_data)

视图层

 1) book_ser = serializers.UserDeserializer(data = request_data) # 数据必须赋值给data
 2) book_ser.is_valid() # 结果为通过或不通过
 3) 不通过返回 book_ser.errors 给前台,通过 book_ser.save() 得到新增对象,再正常返回
class User(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        print(pk)
        if pk:
            try:
                # 用户对象不能直接作为数据返回给前台
                user_obj = models.User.objects.get(pk=pk)
                # 序列化一下用户对象
                user_ser = serializers.UserSerializer(user_obj)
                print(user_ser,type(user_ser))
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_ser.data
                })
            except:
                return Response({
                    'status': 2,
                    'msg': '用户不存在',
                })
        else:
            # 用户对象列表(queryset)不能直接作为数据返回给前台
            user_obj_list = models.User.objects.all()
            # 序列化一下用户对象
            user_ser_data =serializers.UserSerializer(user_obj_list, many=True).data
            return Response({
                'status': 'ok',
                'msg': 0,
                'results':user_ser_data
            })

    #只考虑单增
    def post(self, request, *args, **kwargs):
        # 请求数据
        request_data = request.data
        # 数据是否是合法(增加对象需要一个字典数据)
        if not isinstance(request_data, dict) or request_data == {}:
            return Response({
                'status': 1,
                'msg': '数据有误',
            })
        # 数据类型合法,但数据内容不一定合法,需要校验数据,校验(参与反序列化)的数据需要赋值给data
        book_ser = serializers.UserDeserializer(data = request_data)

        # 序列化对象调用is_valid()完成校验,校验失败的失败信息都会被存储在序列化对象.errors
        if book_ser.is_valid():
            # 校验通过,完成新增
            book_obj = book_ser.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserSerializer(book_obj).data
            })
        else:
            # 校验失败
            return Response({
                'status': 1,
                'msg': book_ser.errors,
            })
ModelSerializer序列化模块
准备工作

首先,先做开发前的配置准备。

  • 1、在根目录新建media文件夹
  • 2、在主项目的settings.py中注册App,修改数据库,国际化等配置,并在末尾添加以下代码,:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

REST_FRAMEWORK = {
}
  • 3、修改路由分发:
    在注册的App中添加urls.py
    在这里插入图片描述
from django.urls import path,re_path
from . import views

urlpatterns = [

]

修改主项目WeStoreurls.py
在这里插入图片描述

from django.contrib import admin
from django.urls import path,re_path
from django.urls import include
from django.conf import settings
from django.views.static import serve


urlpatterns = [
    path('admin/', admin.site.urls),
    # 路由分发
    re_path('api/',include('API.urls')),
    re_path(r'media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]
  • 4、在主项目WeStore__init__.py中初始化MySQL数据库:
import pymysql
pymysql.install_as_MySQLdb()
  • 5、在App中添加自定义全局异常模块exception.py
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.views import Response
from rest_framework import status

def exception_handler(exc, context):
    # drf 的exception做基础处理
    response = drf_exception_handler(exc, context)
    # 为空,自定义二次处理
    if response is None:
        print('%s - %s - %s' % (context['view'], context['request'].method, exc))
        return Response({
            'detail': '服务器错误',
        },status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
    return  response
  • 6、在settings.py中修改异常模块REST_FRAMEWORK的配置:
REST_FRAMEWORK = {
    # 全局配置异常模块
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值