DRF框架 1 DRF入门 1.1 RESTful
REST与技术无关,它是一种 软件架构风格 ,REST是Representational State Transfer的简称,中文翻译为“ 表征状态转移 ”
REST从资源的角度 类审视整个网络,它将分布在网络中某个节点的 资源通过URL进行标识 ,客户端应用通过URL来获取资源 的表征,获得这些表征致使这些应用转变状态
所有的数据,不管是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源 是REST区别与其他架构风格的最本质属性
对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即: 面向资源架构 (ROA:Resource Oriented Architecture)
1.2 API接口和 RESTful API规范 1.2.1 API接口
规定了前端和后端交互规则的URL连接,也就是前后端交互的媒介
1.2.2 为什么要有规范
为了在团队中达成共识、防止个人习惯差异引起的混乱
需要一个大家都认可的规范,减少合作成本
1.2.3 API接口文档
编写接口的用户、使用方法、数据格式等等
可以使用书写、或者使用工具自动生成(coreapi,swagger)
1.2.4 RESTful API规范
1.3 Django-RESTful介绍与安装 1.3.1 介绍
Django rest_framework, 简称 drf, 可以更方便的使用django写出符合 RESTful 规范的接口, (缩减编写api接口的代码)
Django REST framework是一个建立在Django基础之上的Web应用开发框架(Django的一个app),可以快速的开发REST API接口应用
在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程
不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作
REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持
REST framework提供了一个API 的Web可视化界面来方便查看测试接口
1.3.2 安装DRF 1 pip install djangorestframework
1.3.3 配置DRF 在settings.py中添加app
1 2 3 4 INSTALLED_APPS = [ ... 'rest_framework', ]
1.3.4 models.py中书写模型类
1 2 3 4 5 6 7 8 from django.db import models class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) author = models.CharField(max_length=16) publish = models.CharField(max_length=16) # 创建好模型类执行数据库迁移命令
1.3.4 serializer.py新建一个序列化类(该文件也自己创建) 1 2 3 4 5 6 7 from rest_framework.serializers import ModelSerializer from drf_test import models class BookSerializers(ModelSerializer): class Meta: model = models.Book # 指明该序列化器处理的数据字段从模型类Book参考生成 fields = "__all__" # 指明该序列化器包含模型类中的哪些字段,’all‘指明包含所有字段
1.3.5 视图类 1 2 3 4 5 6 7 8 from django.shortcuts import render from rest_framework.viewsets import ModelViewSet from drf_test.serializers import BookSerializers from drf_test import models class BookView(ModelViewSet): serializer_class = BookSerializers # 指明该视图在进行序列化或反序列化时使用的序列化器 queryset = models.Book.objects.all() # 指明该视图集在查询数据时使用的查询集
1.3.6 编写路由 1 2 3 4 5 6 7 8 9 10 from django.contrib import admin from django.urls import path from rest_framework.routers import SimpleRouter from drf_test import views router = SimpleRouter() # 用来处理视图的路由器 router.register('book',views.BookView) # 在路由器中注册视图集 urlpatterns = [ path('admin/', admin.site.urls), ] urlpatterns += router.urls # 将路由器中的所有路由信息拼接到Django中的路由列表中
1.3.7 启动项目开始测试 POST:添加一本书
GET:获取图书
2 DRF功能组件 2.1 认证Authentication 2.1.1 认证配置 在配置文件中配置全局默认的认证方案:
1 2 3 4 5 6 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', # 基本认证 'rest_framework.authentication.SessionAuthentication', # session认证 ) }
也可以在每个视图中通过设置authentication_classes属性来设置
1 2 3 4 5 6 from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.views import APIView class ExampleView(APIView): authentication_classes = (SessionAuthentication, BasicAuthentication) ...
认证失败会有两种可能的返回值
401 Unauthorized 未认证
403 Permission Denied 权限被禁止
权限Permissions
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
在执行视图的dispatch()方法前,会先进行视图访问权限的判断
在通过get_object()获取具体对象时,会进行模型对象访问权限的判断
2.1.2 认证使用 可以在配置文件中设置默认的权限管理类,如:
1 2 3 4 5 REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
如果未指明,则采用如下默认配置
1 2 3 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', )
也可以在具体的视图中通过permission_casses属性来设置,如:
1 2 3 4 5 6 from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView class ExampleView(APIView): permission_classes = (IsAuthenticated,) ...
2.1.3 提供的权限
AllowAny 允许所有用户
IsAuthenticated 仅通过认证的用户
IsAdminUser 仅管理员用户
IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。
案例:
1 2 3 4 5 6 7 8 9 from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView class BookDetailView(RetrieveAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated]
2.1.4 自定义权限 如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
.has_permission(self, request, view) 是否可以访问视图, view表示当前视图对象
.has_object_permission(self, request, view, obj) 是否可以访问数据对象, view表示当前视图, obj为数据对象
案例:
1 2 3 4 5 6 7 8 9 class MyPermission(BasePermission): def has_object_permission(self, request, view, obj): """控制对obj对象的访问权限,此案例决绝所有对对象的访问""" return False class BookInfoViewSet(ModelViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer permission_classes = [IsAuthenticated, MyPermission]
2.2 限流Throttling 2.2.1 限流介绍 可以对接口访问的频次进行限制,以减轻服务器压力。
一般用于付费购买次数,投票等场景使用.
2.2.2 限流使用 可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES和DEFAULT_THROTTLE_RATES进行全局配置
1 2 3 4 5 6 7 8 9 10 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' } }
DEFAULT_THROTTLE_RATES 可以使用 second, minute, hour 或 day来指明周期。
也可以在具体视图中,通过throttle_classes属性来配置,如:
1 2 3 4 5 6 from rest_framework.throttling import UserRateThrottle from rest_framework.views import APIView class ExampleView(APIView): throttle_classes = (UserRateThrottle,) ...
2.2.3 可选限流类 1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用 DEFAULT_THROTTLE_RATES['anon'] 来设置频次
2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用 DEFAULT_THROTTLE_RATES['user'] 来设置频次
3)ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class ContactListView(APIView): throttle_scope = 'contacts' ... class ContactDetailView(APIView): throttle_scope = 'contacts' ... class UploadView(APIView): throttle_scope = 'uploads' ... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.ScopedRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' } }
2.2.4 案例 1 2 3 4 5 6 7 8 9 10 11 from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView from rest_framework.throttling import UserRateThrottle class BookDetailView(RetrieveAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated] throttle_classes = (UserRateThrottle,)
2.3 过滤Filtering 2.3.1 基本用法 对于列表数据,可能需求根据字典及逆行过滤,我们可以通过添加django-filter扩展来增强支持。
1 2 3 4 pip install django-filter # 从数据库中查询数据的过滤 select * from t_emp where name='zs' and sal=2000
在配置文件中增加过滤后端的设置
1 2 3 4 5 6 7 8 9 INSTALLED_APPS = [ ... 'django_filters', # 需要注册应用, ] REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
在视图中添加filter_fields属性,指定可以过滤的字段
1 2 3 4 5 6 class BookListView(ListAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer filter_fields = ('btitle', 'bread') # 127.0.0.1:8000/books/?btitle=西游记
2.3.2 排序 对于列表数据,REST framework提供了OrderingFileter过滤器来帮助我们快速指明数据按照指定字段进行排序
用法
在类视图中设置filter_backends,使用 rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。
示例:
1 2 3 4 5 6 7 class BookListView(ListAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer filter_backends = [OrderingFilter] ordering_fields = ('id', 'bread', 'bpub_date') # 127.0.0.1:8000/books/?ordering=-bread
2.4 分页 2.4.1 基本用法 REST framework提供了分页的支持。
我们可以在配置文件中设置全局的分页方式,如:
1 2 3 4 REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 # 每页数目 }
也可以通过自定义Pagination类,来未视图添加不通分页行为。在视图中通过Pagination_clas属性来指明
1 2 3 4 5 6 7 8 class LargeResultsSetPagination(PageNumberPagination): page_size = 1000 page_size_query_param = 'page_size' max_page_size = 10000 class BookDetailView(RetrieveAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer pagination_class = LargeResultsSetPagination
注意:如果在视图内关闭分页功能,可以按照下面的代码进行设置
2.4.2 可选分页器 1) PageNumberPagination
前端访问网址形式:
1 GET http://api.example.org/books/?page=4
可以在子类中定义的属性:
page_size 每页数目
page_query_param 前端发送的页数关键字名,默认为”page”
page_size_query_param 前端发送的每页数目关键字名,默认为None
max_page_size 前端最多能设置的每页数量
1 2 3 4 5 6 7 8 9 10 11 12 from rest_framework.pagination import PageNumberPagination class StandardPageNumberPagination(PageNumberPagination): page_size_query_param = 'page_size' max_page_size = 10 class BookListView(ListAPIView): queryset = BookInfo.objects.all().order_by('id') serializer_class = BookInfoSerializer pagination_class = StandardPageNumberPagination # 127.0.0.1/books/?page=1&page_size=2
2)LimitOffsetPagination
前端访问网址形式:
1 GET http://api.example.org/books/?limit=100&offset=400
可以在子类中定义的属性:
default_limit 默认限制,默认值与 PAGE_SIZE设置一直
limit_query_param limit参数名,默认’limit’
offset_query_param offset参数名,默认’offset’
max_limit 最大limit限制,默认None
1 2 3 4 5 6 7 8 from rest_framework.pagination import LimitOffsetPagination class BookListView(ListAPIView): queryset = BookInfo.objects.all().order_by('id') serializer_class = BookInfoSerializer pagination_class = LimitOffsetPagination # 127.0.0.1:8000/books/?offset=3&limit=2
2.5 异常处理Exceptions REST framework 提供了异常处理,我们可以自定义异常处理函数
1 2 3 4 5 6 7 8 9 10 11 from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先调用REST framework默认的异常处理方法获得标准错误响应对象 response = exception_handler(exc, context) # 在此处补充自定义的异常处理 if response is not None: response.data['status_code'] = response.status_code return response
在配置文件中声明自定义的异常处理
1 2 3 REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
如果未声明,会采用默认的方式,如下:
1 2 3 REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
例如:
补充上处理关于数据库的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from django.db import DatabaseError def exception_handler(exc, context): response = drf_exception_handler(exc, context) if response is None: view = context['view'] if isinstance(exc, DatabaseError): print('[%s]: %s' % (view, exc)) response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE) return response
REST framework定义的异常
APIException 所有异常的父类
ParseError 解析错误
AuthenticationFailed 认证失败
NotAuthenticated 尚未认证
PermissionDenied 权限决绝
NotFound 未找到
MethodNotAllowed 请求方式不支持
NotAcceptable 要获取的数据格式不支持
Throttled 超过限流次数
ValidationError 校验失败
2.6 自动生成接口文档 REST framework可以自动帮助我们生成接口文档。
接口文档以网页的方式呈现。
自动接口文档能生成的是继承自 APIView及其子类的视图。
2.6.1 安装依赖
2.6.2 设置接口文档访问路径 在总路由中添加接口文档路径。
文档路由对应的视图配置为 rest_framework.documentation.include_docs_urls,
参数 title为接口文档网站的标题。
1 2 3 4 5 6 from rest_framework.documentation import include_docs_urls urlpatterns = [ ... path('docs/', include_docs_urls(title='站点页面标题')) ]
需要在配置文件settings.py中指定schema_class的配置
1 2 3 REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' }
2.6.3 文档描述说明的定义位置 (1)单一方法的视图,可直接使用类视图的文档字符串,如
1 2 3 4 class BookListView(generics.ListAPIView): """ 返回所有图书信息. """
(2) 包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如:
1 2 3 4 5 6 7 8 class BookListCreateView(generics.ListCreateAPIView): """ get: 返回所有图书信息. post: 新建图书. """
(3) 对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): """ list: 返回图书列表数据 retrieve: 返回图书详情数据 latest: 返回最新的图书数据 read: 修改图书的阅读量 """
2.6.4 访问接口文档网页 1) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
2)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:
1 2 3 4 class BookInfo(models.Model): ... bread = models.IntegerField(default=0, verbose_name='阅读量', help_text='阅读量') ...
或
1 2 3 4 5 6 7 8 9 10 class BookReadSerializer(serializers.ModelSerializer): class Meta: model = BookInfo fields = ('bread', ) extra_kwargs = { 'bread': { 'required': True, 'help_text': '阅读量' } }
3 DRF视图家族 Django REST framwork 提供的视图的主要作用:
控制序列化器的执行(检验、保存、转换数据)
控制数据库查询的执行
3.1 两个视图基类 APIView和GenericAPIView
3.1.1 APIView 1 rest_framework.views.APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类
APIView 与 View 的不同之处在于:
传入到视图方法中的是REST framework的 Request对象,而不是Django的 HttpRequeset对象;
视图方法可以返回REST framework的 Response对象,视图会为响应数据设置(render)符合前端要求的格式;
任何 APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性
authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类
throttle_classes 列表或元祖,流量控制类
在 APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
例如:
1 2 3 4 5 6 7 8 9 from rest_framework.views import APIView from rest_framework.response import Response # url(r'^books/$', views.BookListView.as_view()), class BookListView(APIView): def get(self, request): books = BookInfo.objects.all() serializer = BookInfoSerializer(books, many=True) return Response(serializer.data)
3.1.2 GenericAPIView 1 rest_framework.generics.GenericAPIView
继承自 APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。
提供的关于序列化器使用的属性与方法
属性:
serializer_class 指明视图使用的序列化器
方法:
get_serializer_class(self) 当出现一个视图类中调用多个序列化器时,那么可以通过条件来判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。 返回序列化器类,默认返回serializer_class,可以重写,例如:1 2 3 4 def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer
get_serializer(self, args, *kwargs)
返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。
注意,该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
request 当前视图的请求对象
view 当前请求的类视图对象
format 当前请求期望返回的数据格式
提供的关于数据库查询的属性与方法
属性:
方法:
get_queryset(self) 返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
1 2 3 def get_queryset(self): user = self.request.user return user.accounts.all()
get_boject(self)
返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。 在视图中可以调用该方法获取详情信息的模型类对象。 若详情访问的模型类对象不存在,会返回404。 该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
例如:
1 2 3 4 5 6 7 8 9 # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()), class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象 serializer = self.get_serializer(book) return Response(serializer.data)
其他可以设置的属性
pagination_class 指明分页控制类
filter_backends 指明过滤控制后端
3.1.3 DRF的请求对象 REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request 类的对象。
REST framework 提供了Parser 解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request 对象中。
1).data request.data 返回解析之后的请求体数据。类似于Django中标准的 request.POST和 request.FILES属性,但提供如下特性:
包含了解析之后的文件和非文件数据
包含了对POST、PUT、PATCH请求方式解析后的数据
利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params request.query_params与Django标准的 request.GET相同,只是更换了更正确的名称而已。
3)request._request 获取django封装的Request对象
注意:要使用drf提供的Request请求处理对象,必须在编写视图类时继承drf提供的视图基类 from rest_framework.views import APIView
3.1.4 DRF的响应对象 REST framework提供了一个响应类 Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染器)成符合前端需求的类型。
REST framework提供了 Renderer 渲染器,用来根据请求头中的 Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用Content-Type方式处理响应数据,我们可以通过配置来修改默认响应格式。
可以在rest_framework.settings 查找所有的drf默认配置项
1 2 3 4 5 6 REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器,返回json数据 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器,返回调试界面 ) }
1)response的构造方式 Response(data, status=None, template_name=None, headers=None, content_type=None)
data数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用 renderer渲染器处理 data。
data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用 Serializer序列化器序列化处理后(转为了Python字典类型)再传递给 data参数。
参数说明:
data: 为响应准备的序列化处理后的数据;
status: 状态码,默认200;
template_name: 模板名称,如果使用 HTMLRenderer 时需指明;
headers: 用于存放响应头信息的字典;
content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数
2)response对象的状态码 为了方便设置状态码,REST framewrok在 rest_framework.status模块中提供了常用http状态码的常量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 # 1)信息告知 - 1xx HTTP_100_CONTINUE HTTP_101_SWITCHING_PROTOCOLS # 2)成功 - 2xx HTTP_200_OK HTTP_201_CREATED HTTP_202_ACCEPTED HTTP_203_NON_AUTHORITATIVE_INFORMATION HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT HTTP_207_MULTI_STATUS # 3)重定向 - 3xx HTTP_300_MULTIPLE_CHOICES HTTP_301_MOVED_PERMANENTLY HTTP_302_FOUND HTTP_303_SEE_OTHER HTTP_304_NOT_MODIFIED HTTP_305_USE_PROXY HTTP_306_RESERVED HTTP_307_TEMPORARY_REDIRECT # 4)客户端错误 - 4xx HTTP_400_BAD_REQUEST HTTP_401_UNAUTHORIZED HTTP_402_PAYMENT_REQUIRED HTTP_403_FORBIDDEN HTTP_404_NOT_FOUND HTTP_405_METHOD_NOT_ALLOWED HTTP_406_NOT_ACCEPTABLE HTTP_407_PROXY_AUTHENTICATION_REQUIRED HTTP_408_REQUEST_TIMEOUT HTTP_409_CONFLICT HTTP_410_GONE HTTP_411_LENGTH_REQUIRED HTTP_412_PRECONDITION_FAILED HTTP_413_REQUEST_ENTITY_TOO_LARGE HTTP_414_REQUEST_URI_TOO_LONG HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED HTTP_422_UNPROCESSABLE_ENTITY HTTP_423_LOCKED HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS # 5)服务器错误 - 5xx HTTP_500_INTERNAL_SERVER_ERROR HTTP_501_NOT_IMPLEMENTED HTTP_502_BAD_GATEWAY HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
3.1.5 视图类源码解析 1、View类的源码解析 View类核心代码在as_view和dispatch方法中,其中as_view是类方法(@classonlymethod),只能通过类调用,不能通过对象调用,它是类视图的入口点。注意这里调用的时候是通过类名.as_view()调用的。
其中,as_view方法主要执行逻辑:
1 2 3 4 # path('books/', views.BookViwe.as_view()) # views.BookViwe.as_view()的执行结果,是函数内存地址----》view这个函数的内存地址 # 请求来了---》路由匹配成功----》会执行 这个函数---》view(request) # view----》self.dispatch(request)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @classonlymethod def as_view (cls, **initkwargs ): """Main entry point for a request-response process.""" for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr (cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view (request, *args, **kwargs ): self = cls(**initkwargs) if hasattr (self, 'get' ) and not hasattr (self, 'head' ): self.head = self.get self.setup(request, *args, **kwargs) if not hasattr (self, 'request' ): raise AttributeError( "%s instance has no 'request' attribute. Did you override " "setup() and forget to call super()?" % cls.__name__ ) return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) return view
整个as_view方法是一个装饰器函数,它返回内部函数view,所以as_view()执行其实就是内部函数view执行。内部函数view主要逻辑就是:as_view()=>view()=>dispatch()=>相应的http方法
dispatch方法是实例函数,它的主要代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 def dispatch (self, request, *args, **kwargs ): if request.method.lower() in self.http_method_names: handler = getattr (self, request.method.lower(), self.http_method_not_allowed) else : handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
总结 :
dispatch主要完成http请求方法的派发,调用视图类对应实例方法处理用户请求,所有用户需要定义和http请求方法同名的实例方法完成功能,所以一般CBV的模块写法是:
所以:django的CBV本质上来说就是FBV
1 2 3 4 5 6 7 8 9 10 11 12 from django.views import Viewclass IndexView (View ): def get (self,request ): return HttpResponse("get" ) def post (self,request ): return HttpResponse("post" ) def put (self,request ): return HttpResponse("put" ) def delete (self,request ): return HttpResponse("delete" )
2、APIView源码解析
as_view()源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @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 the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super ().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs return csrf_exempt(view)
dispatch()源码
源代码代码步骤
记录各个参数
initializer_request()
initial 加载各种中间件
根据得到的请求方法去处理,或处理异常
最后统一处理response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 def dispatch (self, request, *args, **kwargs ): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers try : self.initial(request, *args, **kwargs) if request.method.lower() in self.http_method_names: handler = getattr (self, request.method.lower(), self.http_method_not_allowed) else : handler = self.http_method_not_allowed 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
initialize_request() 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def initialize_request (self, request, *args, **kwargs ): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
initial(): 源码
步骤:
获取格式化后缀
设置并执行内容协商
设置并进行 决策版本,确定版本
执行认证(重点)
检查权限
检查限流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def initial (self, request, *args, **kwargs ): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
3、GenericAPIView源码解析 GenericAPIView继承自 APIView,也就是在 APIView基础上再做了一层封装
类属性
queryset = None
serializer_class = None
lookup_field = ‘pk’
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
queryset queryset是用来控制视图返回给前端的数据。如果没什么逻辑,可以直接写在视图的类属性中,如果逻辑比较复杂,也可以重写get_queryset方法用来返回一个queryset对象。如果重写了get_queryset,那么以后获取queryset的时候就需要通过调用get_queryset方法。因为queryset` 这个属性只会调用一次,以后所有的请求都是使用他的缓存。
serializer_class serializer_class用来验证和序列化、反序列化数据的。也是可以通过直接设置这个属性,也可以通过重写get_serializer_class来实现。
lookup_field 在检索的时候,根据什么参数进行检索。默认是pk,也就是主键。
lookup_url_kwarg 在检索的url中的参数名称。默认没有设置,跟lookup_field保持一致。
filter_backends 用于过滤查询集的过滤器后端类的列表。默认值与DEFAULT_FILTER_BACKENDS 设置的值相同。
pagination_class 当分页列出结果时应使用的分页类。默认值与 DEFAULT_PAGINATION_CLASS 设置的值相同,即 ‘rest_framework.pagination.PageNumberPagination’。
方法
get_queryset
get_object
get_serializer
get_serializer_class
get_serializer_context
filter_queryset
get_queryset
get_queryset默认是返回数据库全部数据,如果想返回其他数据,需要自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def get_queryset (self ): assert self.queryset is not None , ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset if isinstance (queryset, QuerySet): queryset = queryset.all () return queryset
get_object
该方法是用于在数据检索(通过pk查找)的时候,返回一条数据的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def get_object (self ): queryset = self.filter_queryset(self.get_queryset()) lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(queryset, **filter_kwargs) self.check_object_permissions(self.request, obj) return obj
get_serializer
返回应该用于验证和反序列化输入以及序列化输出的序列化器实例
get_serializer_class
返回用于序列化的类。默认使用 self.serializer_class。如果您需要根据传入请求提供不同的序列化,您可能需要重写它。
3.2 5个视图扩展类 作用:
提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。
这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。
(1) ListModeMixin 列表视图扩展类,提供 list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): # 过滤 queryset = self.filter_queryset(self.get_queryset()) # 分页 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) # 序列化 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
例如:
1 2 3 4 5 6 7 8 from rest_framework.mixins import ListModelMixin class BookListView(ListModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request): return self.list(request)
(2) CreateModeMixin 创建视图扩展类,提供 create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class CreateModelMixin(object): """ Create a model instance. """ def create(self, request, *args, **kwargs): # 获取序列化器 serializer = self.get_serializer(data=request.data) # 验证 serializer.is_valid(raise_exception=True) # 保存 self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save() def get_success_headers(self, data): try: return {'Location': str(data[api_settings.URL_FIELD_NAME])} except (TypeError, KeyError): return {}
(3) RetrieveModelMixin 详情视图扩展类,提供 retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
源代码:
1 2 3 4 5 6 7 8 9 10 class RetrieveModelMixin(object): """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): # 获取对象,会检查对象的权限 instance = self.get_object() # 序列化 serializer = self.get_serializer(instance) return Response(serializer.data)
例如:
1 2 3 4 5 6 class BookDetailView(RetrieveModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): return self.retrieve(request)
(4) UpdateModelMixin 更新视图扩展类,提供 update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供 partial_update(request, *args, **kwargs)方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class UpdateModelMixin(object): """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer): serializer.save() def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs)
(5) DestroyModelMixin 删除视图扩展类,提供 destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
源代码:
1 2 3 4 5 6 7 8 9 10 11 class DestroyModelMixin(object): """ Destroy a model instance. """ def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()
3.3 几个子类视图
(1)CreateAPIView 提供 post 方法
继承自: GenericAPIView、CreateModelMixin
(2)ListAPIView 提供 get 方法
继承自:GenericAPIView、ListModelMixin
(3)RetrieveAPIView 提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
(4)DestoryAPIView 提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
(5)UpdateAPIView 提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView 提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView 提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
3.4 视图集ViewSet 为了让视图代码变得更加简短,让操作同一个模型的视图方法写在一个视图类中, drf提供了视图集[viewsets]。
1.视图集允许开发者自定义类视图方法名
2.视图集允许一个类分配多个不同的路由
3.4.1 常用视图集父类 (1) ViewSet
1 2 3 4 5 继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。 ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{'get':'list'})的映射处理工作。 在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
(2) GenericViewSet
1 2 3 使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。 GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
举例
1 2 3 4 5 6 7 from rest_framework import mixins from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer
url的定义
1 2 3 4 urlpatterns = [ url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})), url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})), ]
(3) ModelViewSet
1 继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
(4) ReadOnlyModelViewSet
继承自 GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
3.4.2 视图集中定义附加action动作 在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from rest_framework import mixins from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def latest(self, request): """ 返回最新的图书信息 """ book = BookInfo.objects.latest('id') serializer = self.get_serializer(book) return Response(serializer.data) def read(self, request, pk): """ 修改图书的阅读量数据 """ book = self.get_object() book.bread = request.data.get('read') book.save() serializer = self.get_serializer(book) return Response(serializer.data)
url的定义
1 2 3 4 5 6 urlpatterns = [ url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})), url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})), url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})), url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})), ]
3.4.3 action属性 1 2 3 4 5 6 7 8 9 10 11 12 from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet from booktest.models import BookInfo from .serializers import BookInfoModelSerializer from rest_framework.response import Response class BookInfoModelViewSet(ModelViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoModelSerializer def get_top_5(self,request): """获取评论值最多的5条数据""" # 操作数据库 print(self.action) # 获取本次请求的视图方法名
通过路由访问到当前方法中,可以看到本次的action就是请求的方法名
3.5 路由Routers 对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。
REST framework提供了两个router
SimpleRouter
DefaultRouter
3.5.1 使用方法 (1) 创建router对象,并注册视图集,例如
1 2 3 4 from rest_framework import routers router = routers.SimpleRouter() router.register(r'books', BookInfoViewSet, base_name='book')
register(prefix, viewset, base_name)
prefix 该视图集的路由前缀
viewset 视图集
base_name 路由名称的前缀
如上述代码会形成的路由如下:
1 2 ^books/$ name: book-list ^books/{pk}/$ name: book-detail
(2) 添加路由数据
可以有两种方式:
1 2 3 4 urlpatterns = [ ... ] urlpatterns += router.urls
或
1 2 3 4 urlpatterns = [ ... url(r'^', include(router.urls)) ]
3.5.2 视图集中附加action的声明 在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用 rest_framework.decorators.action装饰器。
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器可以接收两个参数:
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from rest_framework import mixins from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer # detail为False 表示路径名格式应该为 books/latest/ @action(methods=['get'], detail=False) def latest(self, request): """ 返回最新的图书信息 """ ... # detail为True,表示路径名格式应该为 books/{pk}/read/ @action(methods=['put'], detail=True) def read(self, request, pk): """ 修改图书的阅读量数据 """ ...
由路由器自动为此视图集自定义action方法形成的路由会是如下内容:
1 2 ^books/latest/$ name: book-latest ^books/{pk}/read/$ name: book-read
3.5.3 路由router形成URL的方法 1) SimpleRouter
2)DefaultRouter
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。
4 JWT JWT JSON Web Token(JSON Web令牌)
是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
JWT作用: 授权:一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。它的开销很小并且可以在不同的域中使用。如:单点登录。 信息交换:在各方之间安全地传输信息。JWT可进行签名(如使用公钥/私钥对),因此可确保发件人。由于签名是使用标头和有效负载计算的,因此还可验证内容是否被篡改。
1 2 3 1) jwt = base64(头部).base(载荷).hash256(base64(头部).base(载荷).密钥) 2) base64是可逆的算法、hash256是不可逆的算法 3) 密钥是固定的字符串,保存在服务器
4.1 DRF-JWT 官网:http://getblimp.github.io/django-rest-framework-jwt/
4.1.1 安装 1 pip install djangorestframework-jwt
4.1.2 使用 1 2 3 4 5 from django.urls import path from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('login/', obtain_jwt_token), ]
4.1.3 测试 (案例需要提前准备)
4.1.4 DRF-JWT开发 配置信息:JWT_auth到dev.py中
1 2 3 4 5 6 7 import datetime JWT_AUTH = { # 过期时间 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 自定义认证结果:见下方序列化user和自定义response 'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler', }
序列化user:user/serializers.py(自己创建)
1 2 3 4 5 6 from rest_framework import serializers from . import models class UserModelSerializers(serializers.ModelSerializer): class Meta: model = models.User fields = ['username']
自定义response:user/utils.py
1 2 3 4 5 6 7 8 9 10 from .serializers import UserModelSerializers def jwt_response_payload_handler(token, user=None, request=None): return { 'status': 0, 'msg': 'ok', 'data': { 'token': token, 'user': UserModelSerializers(user).data } }
基于drf-jwt的全局认证:user/authentications.py(自己创建)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import jwt from rest_framework.exceptions import AuthenticationFailed from rest_framework_jwt.authentication import jwt_decode_handler from rest_framework_jwt.authentication import get_authorization_header from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication): def authenticate(self, request): jwt_value = get_authorization_header(request) if not jwt_value: raise AuthenticationFailed('Authorization 字段是必须的') try: payload = jwt_decode_handler(jwt_value) except jwt.ExpiredSignature: raise AuthenticationFailed('签名过期') except jwt.InvalidTokenError: raise AuthenticationFailed('非法用户') user = self.authenticate_credentials(payload) return user, jwt_value
全局启用:settings/dev.py
1 2 3 4 5 6 7 8 9 REST_FRAMEWORK = { # 认证模块 'DEFAULT_AUTHENTICATION_CLASSES': ( 'user.authentications.JSONWebTokenAuthentication', # DRF自带的JWT认证模块 #'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ), }
局部启用禁用:任何一个cvb类首行设置
1 2 3 4 5 6 # 局部禁用 authentication_classes = [] # 局部启用 from user.authentications import JSONWebTokenAuthentication authentication_classes = [JSONWebTokenAuthentication]
自定义代码登录:user/utils.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import re from .models import User from django.contrib.auth.backends import ModelBackend class JWTModelBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: if re.match(r'^1[3-9]\d{9}$', username): user = User.objects.get(mobile=username) else: user = User.objects.get(username=username) except User.DoesNotExist: return None if user.check_password(password) and self.user_can_authenticate(user): return user
配置自定义登录:settings/dev.py
1 AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']
手动签发JWT:
1 2 3 4 5 6 7 from rest_framework_jwt.settings import api_settings jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload)
5 Django DRF序列化器 Serializer 5.1 定义序列化器 Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。
例如,我们已有了一个数据库模型类BookInfo
1 2 3 4 5 6 class BookInfo(models.Model): btitle = models.CharField(max_length=20, verbose_name='名称') bpub_date = models.DateField(verbose_name='发布日期', null=True) bread = models.IntegerField(default=0, verbose_name='阅读量') bcomment = models.IntegerField(default=0, verbose_name='评论量') image = models.ImageField(upload_to='booktest', verbose_name='图片', null=True)
我们想为这个模型类提供一个序列化器,可以定义如下:
1 2 3 4 5 6 7 8 class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名称', max_length=20) bpub_date = serializers.DateField(label='发布日期', required=False) bread = serializers.IntegerField(label='阅读量', required=False) bcomment = serializers.IntegerField(label='评论量', required=False) image = serializers.ImageField(label='图片', required=False)
注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。 serializer是独立于数据库之外的存在。
常用字段类型 常用字段类型 :
字段
字段构造方式
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(max_length=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=)
选项参数 选项参数:
参数名称
作用
max_length
最大长度
min_lenght
最小长度
allow_blank
是否允许为空
trim_whitespace
是否截断空白字符
max_value
最小值
min_value
最大值
通用参数
参数名称
说明
read_only
表明该字段仅用于序列化输出,默认False
write_only
表明该字段仅用于反序列化输入,默认False
required
表明该字段在反序列化时必须输入,默认True
default
反序列化时使用的默认值
allow_null
表明该字段是否允许传入None,默认False
validators
该字段使用的验证器
error_messages
包含错误编号与错误信息的字典
label
用于HTML展示API页面时,显示的字段名称
help_text
用于HTML展示API页面时,显示的字段帮助提示信息
5.2 创建Serializer对象 序列化器有四大功能:
反序列化 : 把请求(request)中参数(字典) ——-> (模型)对象
请求参数校验 :
保存和修改模型对象(数据库):save
序列化: 把(模型)对象 ———> 字典(——->json由response来完成)
定义好Serializer类后,就可以创建Serializer对象了。
Serializer的构造方法为:
1 Serializer(instance=None, data=empty, **kwarg)
说明:
1)用于序列化时,将模型类对象传入instance 参数
2)用于反序列化时,将要被反序列化的数据传入data 参数
3)除了instance和data参数外,在构造Serializer对象时,还可通过context 参数额外添加数据,如
1 serializer = AccountSerializer(account, context={'request': request})
通过context参数附加的数据,可以通过Serializer对象的context属性获取。
使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
序列化器的字段声明类似于我们前面使用过的表单系统。
开发restful api时,序列化器会帮我们把模型数据转换成字典.
drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.
序列化器的使用分两个阶段:
在客户端请求时,使用序列化器可以完成对数据的反序列化。
在服务器响应时,使用序列化器可以完成对数据的序列化。
5.3 序列化 5.3.1 基本使用 (1) 先查询出一个图书对象
1 2 3 from booktest.models import BookInfo book = BookInfo.objects.get(id=2)
(2) 构造序列化对象
1 2 3 from booktest.serializers import BookInfoSerializer serializer = BookInfoSerializer(book)
(3) 获取序列化数据
通过data属性可以获取序列化后的数据
1 2 serializer.data # {'id': 2, 'btitle': '天龙八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None}
(4) 如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明
1 2 3 4 book_qs = BookInfo.objects.all() serializer = BookInfoSerializer(book_qs, many=True) serializer.data # [OrderedDict([('id', 2), ('btitle', '天龙八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飞狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西游记'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]
5.4 反序列化 进行反序列化之前,必须先进行数据验证
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
在获取反序列化的数据前,必须调用 is_valid() 方法进行验证,验证成功返回True,否则返回False。
验证失败,可以通过序列化器对象的errors 属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY 来控制错误字典中的键名。
验证成功,可以通过序列化器对象的validated_data 属性获取数据。
在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。
如我们前面定义过的BookInfoSerializer
1 2 3 4 5 6 7 8 class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名称', max_length=20) bpub_date = serializers.DateField(label='发布日期', required=False) bread = serializers.IntegerField(label='阅读量', required=False) bcomment = serializers.IntegerField(label='评论量', required=False) image = serializers.ImageField(label='图片', required=False)
通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证
1 2 3 4 5 6 7 8 9 10 11 12 13 from booktest.serializers import BookInfoSerializer data = {'bpub_date': 123} serializer = BookInfoSerializer(data=data) serializer.is_valid() # 返回False serializer.errors # {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]} serializer.validated_data # {} data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.errors # {} serializer.validated_data # OrderedDict([('btitle', 'python')])
is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True 参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
1 2 # Return a 400 response if the data was invalid. serializer.is_valid(raise_exception=True)
(1) validate_字段名
对字典进行验证,如:
1 2 3 4 5 6 7 8 class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... def validate_btitle(self, value): if 'django' not in value.lower(): raise serializers.ValidationError("图书不是关于Django的") return value
测试:
1 2 3 4 5 6 from booktest.serializers import BookInfoSerializer data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}
(2) validate
在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,如
1 2 3 4 5 6 from booktest.serializers import BookInfoSerializer data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20} s = BookInfoSerializer(data=data) s.is_valid() # False s.errors # {'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}
(3) validators
在字段中添加validators选项参数,也可以补充验证行为,如
1 2 3 4 5 6 7 8 9 10 11 12 def about_django(value): if 'django' not in value.lower(): raise serializers.ValidationError("图书不是关于Django的") class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django]) bpub_date = serializers.DateField(label='发布日期', required=False) bread = serializers.IntegerField(label='阅读量', required=False) bcomment = serializers.IntegerField(label='评论量', required=False) image = serializers.ImageField(label='图片', required=False)
测试:
1 2 3 4 5 6 from booktest.serializers import BookInfoSerializer data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}
反序列化-保存数据 前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程.这个过程可以把数据转成模型类对象.
可以通过实现create()和update()两个方法来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... def create(self, validated_data): """新建""" return BookInfo(**validated_data) def update(self, instance, validated_data): """更新,instance为要更新的对象实例""" instance.btitle = validated_data.get('btitle', instance.btitle) instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date) instance.bread = validated_data.get('bread', instance.bread) instance.bcomment = validated_data.get('bcomment', instance.bcomment) return instance
如果需要在返回数据对象的时候,也将数据保存到数据库中,则可以进行如下修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... def create(self, validated_data): """新建""" return BookInfo.objects.create(**validated_data) def update(self, instance, validated_data): """更新,instance为要更新的对象实例""" instance.btitle = validated_data.get('btitle', instance.btitle) instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date) instance.bread = validated_data.get('bread', instance.bread) instance.bcomment = validated_data.get('bcomment', instance.bcomment) instance.save() return instance
实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了
1 book = serializer.save()
如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 from db.serializers import BookInfoSerializer data = {'btitle': '封神演义'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.save() # <BookInfo: 封神演义> from db.models import BookInfo book = BookInfo.objects.get(id=2) data = {'btitle': '倚天剑'} serializer = BookInfoSerializer(book, data=data) serializer.is_valid() # True serializer.save() # <BookInfo: 倚天剑> book.btitle # '倚天剑'
说明:
(1)在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到
1 2 # request.user 是django中记录当前登录用户的模型对象 serializer.save(owner=request.user)
(2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新
1 2 # Update `comment` with partial data serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
模型类序列化器 如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
ModelSerializer与常规的Serializer相同,但提供了:
基于模型类自动生成一系列字段
基于模型类自动为Serializer生成validators,比如unique_together
包含默认的create()和update()的实现
定义 比如我们创建一个BookInfoSerializer
1 2 3 4 5 class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = '__all__'
model 指明参照哪个模型类
fields 指明为模型类的哪些字段生成
我们可以在python manage.py shell中查看自动生成的BookInfoSerializer的具体实现
1 2 3 4 5 6 7 8 9 10 >>> from booktest.serializers import BookInfoSerializer >>> serializer = BookInfoSerializer() >>> serializer BookInfoSerializer(): id = IntegerField(label='ID', read_only=True) btitle = CharField(label='名称', max_length=20) bpub_date = DateField(allow_null=True, label='发布日期', required=False) bread = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False) bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False) image = ImageField(allow_null=True, label='图片', max_length=100, required=False)
指定字段 使用fields 来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如
1 2 3 4 5 class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date')
使用exclude可以明确排序掉哪些字段
1 2 3 4 5 class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo exclude = ('image',)
显示指明字段,如:
1 2 3 4 5 6 class HeroInfoSerializer(serializers.ModelSerializer): hbook = BookInfoSerializer() class Meta: model = HeroInfo fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')
指明只读字段
可以通过read_only_fields指明只读字段,既仅用于序列化输出的字段
1 2 3 4 5 6 class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') read_only_fields = ('id', 'bread', 'bcomment')
添加额外参数 我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') extra_kwargs = { 'bread': {'min_value': 0, 'required': True}, 'bcomment': {'min_value': 0, 'required': True}, } # BookInfoSerializer(): # id = IntegerField(label='ID', read_only=True) # btitle = CharField(label='名称', max_length=20) # bpub_date = DateField(allow_null=True, label='发布日期', required=False) # bread = IntegerField(label='阅读量', max_value=2147483647, min_value=0, required=True) # bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=0, required=True)