Django REST framework教程一:序列化
2016-04-08 02:54
666 查看
教程索引目录
Django REST framework的系列教程对于需要通篇了解的同学,可以点击教程索引目录。
入门介绍
本教程将涵盖一个简单的PasteBin1代码高亮的Web API。整个过程,将逐一介绍REST framework的各个组成部件,让你全面理解,组件之间是如何整合的。本教程有点深度,所以在开始之前,你也许会需要几片曲奇饼,一杯你最爱的饮品来压压惊~。如果你只是想有个快速的鸟瞰,也许你该掉头去看看快速入门文档。
留意: 本教材的代码可以在Github中找到:tomchristie/rest-framework-tutorial。完整的代码部署在线上的沙盒版本(sand version)里,用作测试。
搭建一个新的环境
在一切的开始之前,我们会先使用 virtualenv 创建一个新的虚拟环境。这会使我们的软件支持包配置(package configuration),完美的独立于其他正在进行中的项目。virtualenv env source env/bin/activate
现在我们处于一个虚拟环境中了,让我们开始安装我们需要的软件支持包(package)吧。
pip install django pip install djangorestframework pip install pygments #我们将使用它来实现代码高亮
注意:任何时候需要离开虚拟环境,只需输入命令
deactivate。更多的信息,请看 virtualenv文档
开始之前
好了,我们准备好敲代码了。开始之前,我们需要创建一个项目(project)。cd ~ django-admin.py startproject tutorial cd tutorial
然后我们创建一个app,来构建简单的Web API。
python manage.py startapp snippets
我们需要将这个新建的
snippetsapp 和
rest_frameworkapp 添加到
INSTALLED_APPS中。编辑
tutorial/settings.py文件:
INSTALLED_APPS = ( ... 'rest_framework', 'snippets.apps.SnippetsConfig', )
好啦,我们准备好要大干一场了。
创建模型(model)
出于教程的设计考虑,我们将先创建一个简单的Snippets模型,用来存储代码片段。那么来编辑
snippets/models.py文件吧。注意:一个好的编程实践会含有注释。尽管你会在教程的示范代码中找到注释,但为了专注于代码本身,我们在这里将省略注释。
from django.db import models from pygments.lexers import get_all_lexers from pygments.styles import get_all_styles LEXERS = [item for item in get_all_lexers() if item[1]] LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS]) STYLE_CHOICES = sorted((item, item) for item in get_all_styles()) class Snippet(models.Model): created = models.DateTimeField(auto_now_add=True) title = models.CharField(max_length=100, blank=True, default='') code = models.TextField() linenos = models.BooleanField(default=False) language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100) style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100) class Meta: ordering = ('created',)
我们还需要为snippet模型创建数据的表,将模型同步到数据库中,实现初始的迁移(migration)。
python manage.py makemigrations snippets python manage.py migrate
创建序列器(Serializer)类
我们的 Web API 将开始于,为代码片段的实例(instances)提供序列化和反序列化的途径,使之可以转化为,某种表现形式如json。我们可以借助声明序列器(serializer)来实现,类似于Django表单(form)的运作方式。在
snippets路径下,创建文件
serializers.py并以下内容。
from rest_framework import serializers from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES class SnippetSerializer(serializers.Serializer): pk = serializers.IntegerField(read_only=True) title = serializers.CharField(required=False, allow_blank=True, max_length=100) code = serializers.CharField(style={'base_template': 'textarea.html'}) linenos = serializers.BooleanField(required=False) language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python') style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly') def create(self, validated_data): """ 传入验证过的数据, 创建并返回`Snippet`实例。 """ return Snippet.objects.create(**validated_data) def update(self, instance, validated_data): """ 传入验证过的数据, 更新并返回已有的`Snippet`实例。 """ instance.title = validated_data.get('title', instance.title) instance.code = validated_data.get('code', instance.code) instance.linenos = validated_data.get('linenos', instance.linenos) instance.language = validated_data.get('language', instance.language) instance.style = validated_data.get('style', instance.style) instance.save() return instance
序列器(serializer)类的第一部分,告诉REST框架,哪些字段(field),需要被序列化/反序列化。
create()和
update()方法,定义了如何创建和修改,一个有内容的实例对象。这两个方法会在运行
serializer.save()时,被调用。
序列器类非常类似Django的
Form类,在多个字段中,也包含了类似的验证标识(validation flags),如
required,
max_length和
default。
字段标识(flag)也能,控制序列器,在特定情况下,是如何呈现(displayed)的,比如需要渲染(rendering)成HTML。上面的
{'base_template': 'textarea.html'}标识,相当于在Django的
Form类中使用
widget=widgets.Textarea。这尤其在控制可视化API如何来呈现时,特别有用。我们在后面的教程中,会看到这点。
事实上,一会我们可以看到,如何使用
ModelSerializer类, 来节省一些时间。但现在,我们会保持序列器中,每个字段的清晰定义。
让序列器运作起来
在此,让我们先停一停,来熟悉一下,如何使用我们新建的序列器。让我们进入Django shell。python manage.py shell
好了,导入一些包后,为了下步的运作,让我们创建几个代码片段吧
from snippets.models import Snippet from snippets.serializers import SnippetSerializer from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser snippet = Snippet(code='foo = "bar"\n') snippet.save() snippet = Snippet(code='print "hello, world"\n') snippet.save()
现在我们有几个,可用的代码片段实例了。让我们看看,如何来序列化,其中一个实例。
serializer = SnippetSerializer(snippet) serializer.data # {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
此刻,我们将模型实例,转化成了Python的原生数据类型(native datatypes)。要完成序列化的流程,我们将data渲染成
json。
content = JSONRenderer().render(serializer.data) content # '{"pk": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
反序列化也是类似的。首先,我们将一个流(stream)解析(parse)成Python的原生数据类型(native datatypes)…
from django.utils.six import BytesIO stream = BytesIO(content) data = JSONParser().parse(stream)
然后,我们将原生数据类型,还原(restore)成一个被填充完毕(fully populated)的对象实例中。
serializer = SnippetSerializer(data=data) serializer.is_valid() # True serializer.validated_data # OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]) serializer.save() # <Snippet: Snippet object>
注意API的工作形式是如此的相似。这种重复性的相似,会在我们的视图(view)中,用到序列器的时候,变得更加的明显。
除了模型实例,我们也可以将queryset序列化。只需在序列器的参数中加入
many=True。
serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data # [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
使用 模型序列器(ModelSerializers)
在SnippetSerializer类中,重复了许多,在
Snippet模型中的字段定义。如果我们能保持代码简洁,岂不是很好?
就像Django即提供了
Form类,也提供了
ModelForm类, REST framework也有
Serializer类和
ModelSerializer类。
来看看如何,使用
ModelSerializer类,重构我们的序列器。再次打开
snippets/serializers.py, 将
SnippetSerializer类替换为:
class SnippetSerializer(serializers.ModelSerializer): class Meta: model = Snippet fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
序列器有个很好的特性,你可以通过打印序列器的属性,查看序列器对象中所有的字段。在Django shell中(即
python manage.py shell)试试吧:
from snippets.serializers import SnippetSerializer serializer = SnippetSerializer() print(repr(serializer)) # SnippetSerializer(): # id = IntegerField(label='ID', read_only=True) # title = CharField(allow_blank=True, max_length=100, required=False) # code = CharField(style={'base_template': 'textarea.html'}) # linenos = BooleanField(required=False) # language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')... # style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
特别需要一提的是, “ 类并没有什么神奇之处,它只是一种创建序列器的捷径:
自动地声明了一套字段
默认的实现了
create()和
update()方法
用序列器编写普通Django视图
来看看如何使用新建的序列器(Serializer)类来编写一些API视图。到此为止,我们还没有使用过REST framework其他的特性,我们只是编写一个普通的Django视图。我们将从,创建一个HttpResponse的子类开始,这个子类会将任何data渲染并返回为
json。
编辑
snippets/views.py文件,添加下列内容:
from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser from snippets.models import Snippet from snippets.serializers import SnippetSerializer class JSONResponse(HttpResponse): """ An HttpResponse that renders its content into JSON. """ def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs)
我们API的根url,将会成为一个视图,显示所有现存的代码片段,或创建一个新的代码片段。
@csrf_exempt 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 JSONResponse(serializer.data) elif request.method == 'POST': data = JSONParser().parse(request) serializer = SnippetSerializer(data=data) if serializer.is_valid(): serializer.save() return JSONResponse(serializer.data, status=201) return JSONResponse(serializer.errors, status=400)
注意,因为我们需要POST数据,到这个视图的客户端,并没有CSRF令牌(token),所以我们需要为该视图标记为
csrf_exempt。你平时不会做这种事,实际上,相比起这个,REST framework 的视图有着更加合理的行为,但现在我们会这么操作。
我们也需要一个视图,来响应某个单独的代码片段,并且可以获取,更新和删除这个片段。
@csrf_exempt def snippet_detail(request, pk): """ Retrieve, update or delete a code snippet. """ try: snippet = Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: return HttpResponse(status=404) if request.method == 'GET': serializer = SnippetSerializer(snippet) return JSONResponse(serializer.data) elif request.method == 'PUT': data = JSONParser().parse(request) serializer = SnippetSerializer(snippet, data=data) if serializer.is_valid(): serializer.save() return JSONResponse(serializer.data) return JSONResponse(serializer.errors, status=400) elif request.method == 'DELETE': snippet.delete() return HttpResponse(status=204)
最后,我们需要注册这些视图。创建
snippets/urls.py文件:
from django.conf.urls import url from snippets import views urlpatterns = [ url(r'^snippets/$', views.snippet_list), url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail), ]
我们也需要,注册到
tutorial/urls.py文件的根url配置(root urlconf)中,来包含我们的snippets app的URLs。
from django.conf.urls import url, include urlpatterns = [ url(r'^', include('snippets.urls')), ]
需要注意的是,此刻,有一些边缘事件(edge cases),我们没有相应的处理。如果我们发送杂乱的
json, 或一个请求使用了一种请求方法,是我们视图没有涵盖的(如modify),那么我们会出现500 “server error”的响应(response)。总之,现在我们暂时这么做。
测试我们第一个访问Web API的尝试
现在让我们启动提供代码片段服务的服务器吧。退出Django shell…
quit()
… 运行Django的开发者服务器。
python manage.py runserver Validating models... 0 errors found Django version 1.8.3, using settings 'tutorial.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
另起一个终端,我们可以测试我们的服务器。
测试可以使用 curl 或 httpie 。Httpie是一个使用python编写的,非常友好的http客户端。让我们安装它吧。
你可以使用 pip 安装 httpie:
pip install httpie
终于,我们获得了所有片段的列表了:
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" } ]
或者,我们可以根据片段的id,获取某个特定的片段:
http http://127.0.0.1:8000/snippets/2/ HTTP/1.1 200 OK ... { "id": 2, "title": "", "code": "print \"hello, world\"\n", "linenos": false, "language": "python", "style": "friendly" }
同样的,使用浏览器访问这些URLs时,你也会获取相同的json呈现。
我们到哪里了?
目前为止,我们做得还行,我们做的序列化API感觉跟Django的Form API 比较相似,并且我们做了一些普通的Django视图。我们的API视图,现在还没做啥特别的事情。除了响应了json之外,还有一些没能处理的边缘事件,但至少还是个能用的Web API。
我们会在教程的第二部分(tutorial part 3),来看看如何改进。
如果你觉得这个翻译非常有帮助,不妨小额赞助我一下,你的认可,是我的动力!
PasteBin是一个国外的代码片段分享平台,与GitHub不一样的是,它不提供代码托管和版本控制等复杂的功能。 ↩
相关文章推荐
- 解析Silverlight调用WCF/Rest异常的解决方法
- django中使用jquery ajax post数据出现403错误的解决办法(两种方法)
- django使用ajax post数据出现403错误如何解决
- python实现rest请求api示例
- windows+apache+mod_python配置django运行环境
- python Django连接MySQL数据库做增删改查
- django自定义Field实现一个字段存储以逗号分隔的字符串
- 深入学习JavaScript中的Rest参数和参数默认值
- Django框架利用ajax实现批量导入数据功能
- python Django批量导入数据
- 在Python的Django框架中加载模版的方法
- 在Django中使用Sitemap的方法讲解
- 详解Django中的form库的使用
- Django中的CACHE_BACKEND参数和站点级Cache设置
- 剖析Django中模版标签的解析与参数传递
- Python中DJANGO简单测试实例
- Python的Django框架中的数据库配置指南
- 在Django的上下文中设置变量的方法
- 在Python的Django框架中编写编译函数
- 以一个投票程序的实例来讲解Python的Django框架使用