做者:HelloGitHub-追梦人物html
此前在讨论基于模板引擎的开发方式和 django-rest-framework 开发的异同时说过,django-rest-framework 开发和传统的开发方式没有什么不一样,区别仅在于返回的数据格式不一样而已。前端
在基于模板引擎的开发方式中,博客首页文章列表的视图函数多是这样的:web
from django.shortcuts import render
from .models import Post
def index(request):
post_list = Post.objects.all().order_by('-created_time')
return render(request, 'blog/index.html', context={'post_list': post_list})
复制代码
在 django-rest-framework,代码逻辑是同样的,只是在最后返回结果时,返回资源序列化后的结果。数据库
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Post
from .serializers import PostListSerializer
@api_view(http_method_names=["GET"])
def index(request):
post_list = Post.objects.all().order_by('-created_time')
serializer = PostListSerializer(post_list, many=True)
return Response(serializer.data, status=status.200)
复制代码
暂且忽略掉资源序列化器 PostListSerializer
,咱们接下来会实现它,先把注意力放在主体逻辑上。django
首先,咱们从 rest_framework.decorators
中导入了 api_view 装饰器,并用它装饰了 index 视图函数,使其成为一个 RESTful API 视图函数。api
为何须要这个视图函数装饰器呢?以前说过,django-rest-framework 为 API 的开发提供了丰富的功能,包括内容协商、认证和鉴权、限流等等。这些过程 django 默认的视图函数在处理 HTTP 请求时是没有提供的,而通过 api_view
装饰后的视图,则提供了上述所有功能。浏览器
不过咱们这里并无看到任何内容协商、认证和鉴权、限流代码逻辑和配置,这是为何呢?缘由隐藏在 Python 的装饰器魔法里,django-rest-framework 对于上述功能有一套默认的处理逻辑,所以咱们不须要进行任何配置,仅需使用 api_view
装饰一个 django 视图函数,全部功能所有自动开启。前端框架
视图函数里咱们先从数据库获取文章列表资源,而后使用序列化器对其进行序列化,序列化后的数据存在 data
属性里,咱们把它传递给 HTTP 响应类 Response
,并将这个响应返回。服务器
注意这个 Response
是从 rest_framework.response
中导入的,它相似于 django 的 HTTPResponse 响应类。实际上,这个类是 django-rest-framework 对 django 的模板响应类(SimpleTemplateResponse)的拓展(具体的细节能够不用了解,只要知道 django 使用它来渲染模板并构造 HTTP 响应便可),一般在 RESTful API 的视图函数中咱们都会返回这个类,而不是 django 的 HTTP 响应类。此外,经过传入 status 参数,指定 HTTP 响应的状态码。架构
小贴士
请了解经常使用的 HTTP 状态码。在 RESTful 架构中,客户端经过 HTTP 请求动词表征对资源的操做意图,而服务端则使用 HTTP 状态码表示资源操做的结果。经常使用状态码及其含义以下:
200:一般表示请求成功。
201:表示资源建立成功。
400:表示客户端请求错误。
401:没有提供身份认证信息
403:没有操做权限
404 :访问的资源不存在
405:不支持的 HTTP 请求方法
500:服务器内部错误
HTTP 请求和响应过程,django-rest-framework 已经帮咱们处理。可是资源的序列化,框架是没法自动化完成的,框架提供了基本的序列化器,咱们须要自定义序列化逻辑。因此,让咱们来定义 PostListSerializer
序列化器,用它来序列化文章列表。
序列化器由一系列的序列化字段(Field)组成,序列化字段的做用是,在序列化资源时,将 Python 数据类型转为原始数据类型(一般为字符类型或者二进制类型),以便在客户端和服务端之间传递;反序列化时,将原始数据类型转为 Python 数据类型。在转换过程当中,还会进行数据合法性的校验。
先来看一个简单的例子(摘自 django-rest-framework 官网示例),理解序列化器的工做原理和功能。假设咱们有一个 Python 类 Comment
:
from datetime import datetime
class Comment(object):
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
复制代码
根据 Comment
类 3 个属性的类型,定义一个序列化器,用于数据序列化和反序列化。咱们在上一步教程的 交流的桥梁:评论功能 中介绍过表单(Form)的定义。实际上,django-rest-framework 序列化器的设计参考了 django 表单的设计。序列化器和表单也有不少类似功能,好比对输入数据进行校验等。序列化器的代码以下:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
复制代码
自定义的序列化器都要继承 serializers.Serializer
基类,基类提供了数据序列化和反序列化的逻辑。根据被序列化对象的属性的数据类型,须要指定相应的序列化字段(Serializer Field)。django-rest-framework 提供了不少经常使用的序列化字段,例如本例中用于序列化 email 数据格式的 EmailField
,用于序列化字符型数据格式的 CharField
,用于序列化日期格式的 DateTimeField
。在实际项目中,应该根据数据类型,选择合适的序列化字段。所有序列化字段,能够参考官方文档 Serializer fields。
有了序列化器,就能够将 Comment
对象序列化了,序列化器用法以下:
>>> serializer = CommentSerializer(comment)
>>> serializer.data
# 输出:
{'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
复制代码
首先将须要序列化的对象(comment)传入序列化器(CommentSerializer),构造一个序列化器对象(serializer),访问序列化器对象的 data 属性,就能够获得序列化后的数据。
被序列化对象序列化后的数据是一个扁平的 Python 字典,字典中的数据描述了这个对象资源。有了序列化生成的 Python 字典,咱们就能够将字典数据进一步格式化为 JSON 字符串或者 XML 文档字符串,在客户端和服务端之间传输。试想,客户端服务端一般都经过 HTTP 协议传输数据,传输的数据只能是字符串或者二进制数据,不可能将一个 Python 的对象直接传递,这就是为何要序列化的缘由。一端接收到序列化的数据后,若是有须要,能够对数据进行反序列化,从新恢复为 Python 对象。
以上就是一个标准序列化器的定义。其关键点在于,根据被序列化对象属性的数据类型,选择合适的序列化字段。回顾咱们在上一步教程的 交流的桥梁:评论功能 中对评论表单的定义,咱们经过继承 ModelForm
定义了表单,而并无显示地指定表单字段的类型。缘由在于,对于 django 中的模型(Model),已经有了定义其数据类型的模型字段,所以 django 表单能够根据关联的模型,自动推测须要使用的表单字段,在背后帮咱们完成表单字段的选择,简化了表单的定义。
和表单相似,django-rest-framework 的序列化器也能够根据关联的模型,自动检测被序列化模型各个属性的数据类型,推测须要使用的序列化字段,无需咱们显示定义。此时,自定义的序列化器再也不继承标准的 Serializer
,而是继承其子类,ModelSerializer
。
咱们来编写文章(Post)模型的序列化器代码。按照习惯,序列化器的代码位于相应应用的 serializers.py 模块中,所以在 blog 应用下新建一个 serializers.py 文件,写上以下代码:
from rest_framework import serializers
from .models import Post
class PostListSerializer(serializers.ModelSerializer):
category = CategorySerializer()
author = UserSerializer()
class Meta:
model = Post
fields = [
'id',
'title',
'created_time',
'excerpt',
'category',
'author',
'views',
]
复制代码
使用 ModelSerializer
时,只须要在序列化器的内部类 Meta
中指定关联的模型,以及须要序列化的模型属性,django-rest-framework 就会根据各个属性的数据类型,自动推测须要使用的系列化字段,从而生成标准的序列化器。事实上,咱们能够来看一下 django-rest-framework 最终生成的序列化器长什么样子:
class PostListSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(label='标题', max_length=70)
created_time = DateTimeField(label='建立时间', required=False)
excerpt = CharField(allow_blank=True, label='摘要', max_length=200, required=False)
category = CategorySerializer()
author = UserSerializer()
复制代码
还须要注意一点,title
、created_time
、views
这些属性都是原始的数据类型(字符型、日期型、整数类型)。而对于文章关联的 category
、author
,它们自己也是一个对象,django-rest-framework 就没法推测该使用什么类型的系列化字段来序列化它们了。因此这里咱们按照标准序列化器的定义方式,将这两个属性的系列化字段分别定义为 CategorySerializer
、UserSerializer
,意思是告诉 django-rest-framework,请使用 CategorySerializer
和 UserSerializer
来序列化关联的 category
和 author
。实际上,序列化器自己也是一个序列化字段。固然,CategorySerializer
和 UserSerializer
目前还不存在,咱们来定义他们:
from django.contrib.auth.models import User
from .models import Category, Post
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id',
'name',
]
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
]
class PostListSerializer(serializers.ModelSerializer):
# ...
复制代码
再来回顾一下咱们的 API 视图函数代码:
@api_view(http_method_names=["GET"])
def index(request):
post_list = Post.objects.all().order_by('-created_time')
serializer = PostListSerializer(post_list, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
复制代码
注意这里 PostListSerializer
的用法,构造序列化器时能够传入单个对象,序列化器会将其序列化为一个字典;也能够传入包含多个对象的可迭代类型(这里的 post_list 是一个 django 的 QuerySet),此时须要设置 many
参数为 True
序列化器会依次序列化每一项,返回一个列表。
给 api_view
装饰器传入 http_method_names
参数指定容许访问该 API 视图的 HTTP 方法。
如今咱们已经有了视图函数,最后,咱们须要给这个视图函数绑定 URL,在 blog 应用下的 urls.py 中加入绑定的代码:
path('api/index/', views.index)
复制代码
启动开发服务器,打开浏览器访问 http://127.0.0.1:8000/api/index/ ,能够看到接口返回了文章列表 JSON 格式的数据(默认为 JSON)。
目前来讲,这个接口其实做用不大。不过在后续的教程中,咱们学习前端框架 Vue,那个时候,RESTful API 就有了它的用武之地了。
回顾一下 index API 视图函数的基本逻辑:
这实际上是访问序列型的资源比较常见的逻辑,咱们知道,django 专门为这种在 Web 开发中经常使用的逻辑提供了一系列基于类的通用视图,以提升代码的复用性和减小代码量。只是 django 的通用视图适用于基于模板引擎的开发方式,一样的,django-rest-framework 也提供了专门针对 RESTful API 开发过程当中经常使用逻辑的类视图通用函数。接下来,让咱们使用 django-rest-framework 提供的通用类视图,将首页 API 的视图函数改成类视图。