在本篇中, 我们将通过建立一个代码黏贴板(pastebin), 来熟悉组成REST framework的各组成部分, 并了解这些部件是如何相互协调工作的.
1. 环境设置
首先我们使用virtualenvwrapper创建新的virtualenv, 并安装需要的代码库:
mkvirtualenv env pip install django pip install djangorestframework pip install pygments # 用于代码高亮
2. Django项目设置
我们建立Django项目tutorial, 和app snippets
django-admin.py startproject tutorial cd tutorial python manage.py startapp snippets
设置settings.py:
# tutorial/settings.py DATABASES = { ‘default‘: { ‘ENGINE‘: ‘django.db.backends.postgresql_psycopg2‘, ‘NAME‘: ‘database_name‘, ‘USER‘: ‘database_user‘, ‘PASSWORD‘: ‘database_password‘, ‘HOST‘: ‘‘, ‘PORT‘: ‘‘ } } INSTALLED_APPS = ( ... ‘rest_framework‘, ‘snippets‘, )
设置urls.py, 将新建的snippet app中的urls.py加入到其中:
# tutorial/urls.py urlpatterns = patterns(‘‘, url(r‘^‘, include(‘snippets.urls‘)), )
3. 创建Model
我们建立Snippet Model用于储存代码:
# snippet/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‘,)
启动django服务器:
python manage.py syncdb
4. 创建Serializer
第一步我们需要为我们的API提供序列化和反序列化的方法, 将snippet实例转为json等方式呈现数据. 我们可以使用Serializer达到这一目的, Serializer和django forms十分相似. 我们建立snippet/serializers.py文件:
# snippets/serializers.py from django.forms import widgets from rest_framework import serializers from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES class SnippetSerializer(serializers.Serializer): pk = serializers.Field() # `Field` 是无类型, 只读的. title = serializers.CharField(required=False, max_length=100) code = serializers.CharField(widget=widgets.Textarea, max_length=100000) linenos = serializers.BooleanField(required=False) language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default=‘python‘) style = serializers.ChoiceField(choices=STYLE_CHOICES, default=‘friendly‘) def restore_object(self, attrs, instance=None): """ 创建或更新一个snippet实例, 返回该snippet实例 如果不定义该function, 则反序列化时将返回一个包括所有field的dict """ if instance: # 更新已存在的snippet实例 instance.title = attrs.get(‘title‘, instance.title) instance.code = attrs.get(‘code‘, instance.code) instance.linenos = attrs.get(‘linenos‘, instance.linenos) instance.language = attrs.get(‘language‘, instance.language) instance.style = attrs.get(‘style‘, instance.style) return instance # Create new instance return Snippet(**attrs)
以上代码第一部分定义了序列化和反序列化的项, 第二部分restore_object function则定义了符合要求的已序列化snippet实例如何反序列化.
注意, 我们也可以使用在django form中使用的参数, 比如widget=widgets.Textarea. 这些参数可以控制serializer如何显示为HTML form, 尤其是在构建可浏览的API时, 十分有用, 我们也会在今后的博文中详细介绍.
如果想快速的构建serializer, 我们也可以使用ModelSerializer, 我们会在稍后介绍:
5. 使用serializer
在继续完成该项目前, 我们先熟悉一下serializer, 是有manage.py shell启动django shell:
python manage.py shell
import必要的代码库并创建2个snippet实例:
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‘}
以上代码已将snippet实例转化为Python基本数据类型, 接下来我们完成序列化:
content = JSONRenderer().render(serializer.data) content # ‘{"pk": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}‘
反序列化也是类似的, 首先将stream转为python基本类型:
# 根据我们使用的是 python 2 或是 python 3 # 这一import会自动引入 `StringIO.StringIO` 或 `io.BytesIO` from rest_framework.compat import BytesIO stream = BytesIO(content) data = JSONParser().parse(stream)
然后我们将它转化为snippet实例:
serializer = SnippetSerializer(data=data) serializer.is_valid() # True serializer.object # <Snippet: Snippet object>
可见, serializer和django form 有多么相似, 当我们写view时, 这一相似性会更加明显.
当我们输入参数many=True时, serializer还能序列化queryset:
serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data # [{‘pk‘: 1, ‘title‘: u‘‘, ‘code‘: u‘foo = "bar"\n‘, ‘linenos‘: False, ‘language‘: u‘python‘, ‘style‘: u‘friendly‘}, {‘pk‘: 2, ‘title‘: u‘‘, ‘code‘: u‘print "hello, world"\n‘, ‘linenos‘: False, ‘language‘: u‘python‘, ‘style‘: u‘friendly‘}]
6. 使用 ModelSerializers
在我们的SnippetSerializer中, 包含了许多与model重复的field, 那么是否能将这些代码变得更紧凑呢? 当然可以!
就像Django提供ModelForm一样, django_rest_framework提供了ModelSerializer.
我们修改之前的SnippetSerializer:
#snippets/serializers.py class SnippetSerializer(serializers.ModelSerializer): class Meta: model = Snippet fields = (‘id‘, ‘title‘, ‘code‘, ‘linenos‘, ‘language‘, ‘style‘)
7. 在Views中使用Serializer
以下代码中, 为了更好的理解Serializer, 我们只使用django_rest_framework的Serializer部件和Django最基本的function_base_view.
首先创建能返回json数据的HttpResponse:
# 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): """ 将内容转为JSON格式的HttpResponse """ def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs[‘content_type‘] = ‘application/json‘ super(JSONResponse, self).__init__(content, **kwargs)
我们API的根目录是一个list view, 用于展示所有存在的snippet, 或建立新的snippet:
# snippets/views.py @csrf_exempt def snippet_list(request): """ 展示所有存在的snippet, 或建立新的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, 因此使用了csrf_exempt. 这不是通常应该做的, 而且django_rest_framework默认使用了更为安全的方式.
用于展示, 更新或删除的view:
# snippets/views.py @csrf_exempt def snippet_detail(request, pk): """ 展示, 更新或删除一个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)
最后修改urls.py, 使这些view通电
# snippets/urls.py from django.conf.urls import patterns, url urlpatterns = patterns(‘snippets.views‘, url(r‘^snippets/$‘, ‘snippet_list‘), url(r‘^snippets/(?P<pk>[0-9]+)/$‘, ‘snippet_detail‘), )
需要注意的是, 我们还有许多错误处理没有涉及, 例如提交错误的json, 使用view不支持的http method等. 同样处于更好的理解serializer的目的, 这些错误都会返回500错误页.
8. 测试
首先我们推出shell:
quit()
启动django server:
python manage.py runserver
在另外的终端中:
curl http://127.0.0.1:8000/snippets/ [{"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获取一个snippet:
curl http://127.0.0.1:8000/snippets/2/ {"id": 2, "title": "", "code": "print \"hello, world\"\n", "linenos": false, "language": "python", "style": "friendly"}
同样, 我们也可以使用浏览器测试这些API.
原文链接: http://www.weiguda.com/blog/19/