博客
关于我
DRF对Django请求响应做了技术升级
阅读量:422 次
发布时间:2019-03-06

本文共 5820 字,大约阅读时间需要 19 分钟。

Django视图是用来处理请求和响应的,Django默认是按Form和Template来设计的,如果要处理以JSON格式为主的RESTful API,那么就需要对Django请求和响应的处理代码进行优化改造,本文就来介绍DRF在这一部分的技术升级。

Request

DRF把Django的HttpRequest扩展成了Request

其中最核心的属性是request.data,它和request.POST的区别如下:

request.POST  # 只处理表单(Form)数据,只支持POST方法request.data  # 处理任何数据,支持POST、PUT、PATCH方法

Response

DRF的Response继承自Django的django.template.response.SimpleTemplateResponse

Response可以根据客户端的请求render合适的content type:

return Response(data)

我摘取了rendered_content()函数的代码:

@propertydef rendered_content(self):    renderer = getattr(self, 'accepted_renderer', None)    accepted_media_type = getattr(self, 'accepted_media_type', None)    context = getattr(self, 'renderer_context', None)    assert renderer, ".accepted_renderer not set on Response"    assert accepted_media_type, ".accepted_media_type not set on Response"    assert context is not None, ".renderer_context not set on Response"    context['response'] = self    media_type = renderer.media_type    charset = renderer.charset    content_type = self.content_type    if content_type is None and charset is not None:        content_type = "{}; charset={}".format(media_type, charset)    elif content_type is None:        content_type = media_type    self['Content-Type'] = content_type    ret = renderer.render(self.data, accepted_media_type, context)    if isinstance(ret, str):        assert charset, (            'renderer returned unicode, and did not specify '            'a charset value.'        )        return ret.encode(charset)    if not ret:        del self['Content-Type']    return ret

Status codes

如果在代码中直接写数字形式的状态码如400,是不容易阅读的,于是DRF提供了标识符如HTTP_400_BAD_REQUEST来替代。我列一些常见的状态码标识符:

HTTP_200_OK = 200HTTP_201_CREATED = 201HTTP_204_NO_CONTENT = 204HTTP_400_BAD_REQUEST = 400HTTP_401_UNAUTHORIZED = 401HTTP_403_FORBIDDEN = 403HTTP_404_NOT_FOUND = 404HTTP_405_METHOD_NOT_ALLOWED = 405HTTP_500_INTERNAL_SERVER_ERROR = 500HTTP_502_BAD_GATEWAY = 502HTTP_503_SERVICE_UNAVAILABLE = 503HTTP_504_GATEWAY_TIMEOUT = 504

全部的状态码标识符可以在rest_framework.status模块中看到。

@api_view和APIView

DRF对API视图做了2个封装:

  1. @api_view用于函数视图。
  2. APIView用于类视图。

它们提供了一些新功能,比如:

  • 检查请求是Request对象
  • 添加上下文到Response对象
  • 返回请求错误如405 Method Not Allowed
  • request.data格式有误时,抛出ParseError异常

改造views.py

接着就用上面这几个新实现对我们之前写的snippets/views.py进行改造:

from rest_framework import statusfrom rest_framework.decorators import api_viewfrom rest_framework.response import Responsefrom snippets.models import Snippetfrom snippets.serializers import SnippetSerializer@api_view(['GET', 'POST'])def snippet_list(request):    """    List all code snippets, or create a new snippet.    """    if request.method == 'GET':        snippets = Snippet.objects.all()        serializer = SnippetSerializer(snippets, many=True)        return Response(serializer.data)    elif request.method == 'POST':        serializer = SnippetSerializer(data=request.data)        if serializer.is_valid():            serializer.save()            return Response(serializer.data, status=status.HTTP_201_CREATED)        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    @api_view(['GET', 'PUT', 'DELETE'])def snippet_detail(request, pk):    """    Retrieve, update or delete a code snippet.    """    try:        snippet = Snippet.objects.get(pk=pk)    except Snippet.DoesNotExist:        return Response(status=status.HTTP_404_NOT_FOUND)    if request.method == 'GET':        serializer = SnippetSerializer(snippet)        return Response(serializer.data)    elif request.method == 'PUT':        serializer = SnippetSerializer(snippet, data=request.data)        if serializer.is_valid():            serializer.save()            return Response(serializer.data)        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    elif request.method == 'DELETE':        snippet.delete()        return Response(status=status.HTTP_204_NO_CONTENT)

改动点有这些,添加了@api_view,如:

@api_view(['GET', 'POST'])

使用了状态码标识符,如:

status.HTTP_404_NOT_FOUND

使用request.data替代了 data = JSONParser().parse(request),如:

serializer = SnippetSerializer(data=request.data)

使用Response()替代了JsonResponse(),如:

return Response(serializer.data, status=status.HTTP_201_CREATED)

request.dataResponse()能根据请求的JSON自动处理content type。

添加后缀格式(可选)

既然DRF能自动处理content type,那么也可以给URL指定具体的后缀格式,比如http://example.com/api/items/4.json。具体添加步骤是,先给view增加1个可选参数format

def snippet_list(request, format=None):
def snippet_detail(request, pk, format=None):

再更新snippets/urls.py,添加format_suffix_patterns

from django.urls import pathfrom rest_framework.urlpatterns import format_suffix_patternsfrom snippets import viewsurlpatterns = [    path('snippets/', views.snippet_list),    path('snippets/
', views.snippet_detail),]urlpatterns = format_suffix_patterns(urlpatterns)

这并不是必须的,实际上也无需这么做。

测试API

http http://127.0.0.1:8000/snippets/HTTP/1.1 200 OK...[  {    "id": 1,    "title": "",    "code": "foo = \"bar\"\n",    "linenos": false,    "language": "python",    "style": "friendly"  },  {    "id": 2,    "title": "",    "code": "print(\"hello, world\")\n",    "linenos": false,    "language": "python",    "style": "friendly"  }]

跟之前的结果一样。再分别用form和json试试:

# POST using form datahttp --form POST http://127.0.0.1:8000/snippets/ code="print(123)"{  "id": 3,  "title": "",  "code": "print(123)",  "linenos": false,  "language": "python",  "style": "friendly"}# POST using JSONhttp --json POST http://127.0.0.1:8000/snippets/ code="print(456)"{    "id": 4,    "title": "",    "code": "print(456)",    "linenos": false,    "language": "python",    "style": "friendly"}

API文档

DRF提供了可视化的API HTML文档,把API URL在浏览器中打开即可看到:

东方说

最近测试开发和业务测试的话题频频出现在TesterHome论坛上,讨论激烈,我觉得从公司的角度来说,只会关注员工的产出有没有给公司带来价值,无论技术多厉害,不能创造价值终究是会优先被裁的。从个人的角度来说,只会业务测试的出路肯定是会越来越窄的,努力提高技术,辅助业务测试,同时提升效率,才是更好的发展方向。千万要谨慎选择只做纯测试工具,要依托于业务,让技术落地,在业务中发挥技术的价值,产生从业务到技术,从技术到业务的良好循环。当然,会技术是个大前提,对技术的学习不能停,比如Django REST framework。

参考资料:

转载地址:http://ouyuz.baihongyu.com/

你可能感兴趣的文章
mysql 数据库备份及ibdata1的瘦身
查看>>
MySQL 数据库备份种类以及常用备份工具汇总
查看>>
mysql 数据库存储引擎怎么选择?快来看看性能测试吧
查看>>
MySQL 数据库操作指南:学习如何使用 Python 进行增删改查操作
查看>>
MySQL 数据库的高可用性分析
查看>>
MySQL 数据库设计总结
查看>>
Mysql 数据库重置ID排序
查看>>
Mysql 数据类型一日期
查看>>
MySQL 数据类型和属性
查看>>
mysql 敲错命令 想取消怎么办?
查看>>
Mysql 整形列的字节与存储范围
查看>>
mysql 断电数据损坏,无法启动
查看>>
MySQL 日期时间类型的选择
查看>>
Mysql 时间操作(当天,昨天,7天,30天,半年,全年,季度)
查看>>
MySQL 是如何加锁的?
查看>>
MySQL 是怎样运行的 - InnoDB数据页结构
查看>>
mysql 更新子表_mysql 在update中实现子查询的方式
查看>>
MySQL 有什么优点?
查看>>
mysql 权限整理记录
查看>>
mysql 权限登录问题:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
查看>>