序列化Serialization
1. 設(shè)置一個(gè)新的環(huán)境
在我們開始之前, 我們首先使用virtualenv要?jiǎng)?chuàng)建一個(gè)新的虛擬環(huán)境,以使我們的配置和我們的其他項(xiàng)目配置徹底分開。
$mkdir ~/env$virtualenv ~/env/tutorial$source ~/env/tutorial/bin/avtivate
現(xiàn)在我們處在一個(gè)虛擬的環(huán)境中,開始安裝我們的依賴包
$pip install django$pip install djangorestframework$pip install pygments ////使用這個(gè)包,做代碼高亮顯示
需要退出虛擬環(huán)境時(shí),運(yùn)行deactivate。更多信息,irtualenv document
2. 開始
環(huán)境準(zhǔn)備好只好,我們開始創(chuàng)建我們的項(xiàng)目
$ cd ~$ django-admin.py startproject tutorial$ cd tutorial
項(xiàng)目創(chuàng)建好后,我們?cè)賱?chuàng)建一個(gè)簡(jiǎn)單的app
$python manage.py startapp snippets
我們使用sqlite3來(lái)運(yùn)行我們的項(xiàng)目tutorial,編輯tutorial/settings.py, 將數(shù)據(jù)庫(kù)的默認(rèn)引擎engine改為sqlite3, 數(shù)據(jù)庫(kù)的名字NAME改為tmp.db
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'tmp.db', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', }} 同時(shí)更改settings.py文件中的INSTALLD_APPS,添加我們的APP snippets和rest_framework
INSTALLED_APPS = ( ... 'rest_framework', 'snippets',)
在tutorial/urls.py中,將snippets app的url包含進(jìn)來(lái)
urlpatterns = patterns('', url(r'^', include('snippets.urls')),) 3. 創(chuàng)建Model
這里我們創(chuàng)建一個(gè)簡(jiǎn)單的nippets model,目的是用來(lái)存儲(chǔ)代碼片段。
from django.db import modelsfrom pygments.lexers import get_all_lexersfrom pygments.styles import get_all_stylesLEXERS = [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) 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',)
完成model時(shí),記得sync下數(shù)據(jù)庫(kù)
python manage.py syncdb
4. 創(chuàng)建序列化類
我們要使用我們的web api,要做的第一件事就是序列化和反序列化, 以便snippets實(shí)例能轉(zhuǎn)換為可表述的內(nèi)容,例如json. 我們聲明一個(gè)可有效工作的串行器serializer。在snippets目錄下面,該串行器與django 的表單形式很類似。創(chuàng)建一個(gè)serializers.py ,并將下面內(nèi)容拷貝到文件中。
from django.forms import widgetsfrom rest_framework import serializersfrom snippets.models import Snippetclass SnippetSerializer(serializers.Serializer): pk = serializers.Field() # Note: `Field` is an untyped read-only field. max_length=100) code = serializers.CharField(widget=widgets.Textarea, max_length=100000) linenos = serializers.BooleanField(required=False) language = serializers.ChoiceField(choices=models.LANGUAGE_CHOICES, default='python') style = serializers.ChoiceField(choices=models.STYLE_CHOICES, default='friendly') def restore_object(self, attrs, instance=None): """ Create or update a new snippet instance. """ if instance: # Update existing instance instance.title = attrs['title'] instance.code = attrs['code'] instance.linenos = attrs['linenos'] instance.language = attrs['language'] instance.style = attrs['style'] return instance # Create new instance return Snippet(**attrs)
該序列化類的前面部分,定義了要序列化和反序列化的類型,restore_object 方法定義了如何通過(guò)反序列化數(shù)據(jù),生成正確的對(duì)象實(shí)例。
我們也可以使用ModelSerializer來(lái)快速生成,后面我們將節(jié)省如何使用它。
5. 使用 Serializers
在我們使用我們定義的SnippetsSerializers之前,我們先熟悉下Snippets.
$python manage.py shell
進(jìn)入shell終端后,輸入以下代碼:
from snippets.models import Snippetfrom snippets.serializers import SnippetSerializerfrom rest_framework.renderers import JSONRendererfrom rest_framework.parsers import JSONParsersnippet = Snippet(code='print "hello, world"/n')snippet.save()
我們現(xiàn)在獲得了一個(gè)Snippets的實(shí)例,現(xiàn)在我們對(duì)他進(jìn)行以下序列化
serializer = SnippetSerializer(snippet)serializer.data# {'pk': 1, 'title': u'', 'code': u'print "hello, world"/n', 'linenos': False, 'language': u'python', 'style': u'friendly'} 這時(shí),我們將該實(shí)例轉(zhuǎn)成了python原生的數(shù)據(jù)類型。下面我們將該數(shù)據(jù)轉(zhuǎn)換成json格式,以完成序列化:
content = JSONRenderer().render(serializer.data)content# '{"pk": 1, "title": "", "code": "print //"hello, world//"//n", "linenos": false, "language": "python", "style": "friendly"}' 反序列化也很簡(jiǎn)單,首先我們要將一個(gè)輸入流(content),轉(zhuǎn)換成python的原生數(shù)據(jù)類型
import StringIOstream = StringIO.StringIO(content)data = JSONParser().parse(stream)
然后我們將該原生數(shù)據(jù)類型,轉(zhuǎn)換成對(duì)象實(shí)例
serializer = SnippetSerializer(data=data)serializer.is_valid()# Trueserializer.object# <Snippet: Snippet object>
注意這些API和django表單的相似處。這些相似點(diǎn), 在我們講述在view中使用serializers時(shí)將更加明顯。
6. 使用 ModelSerializers
SnippetSerializer使用了許多和Snippet中相同的代碼。如果我們能把這部分代碼去掉,看上去將更佳簡(jiǎn)潔。
類似與django提供Form類和ModelForm類,Rest Framework也包含了Serializer 類和 ModelSerializer類。
打開snippets/serializers.py ,修改SnippetSerializer類:
class SnippetSerializer(serializers.ModelSerializer): class Meta: model = Snippet fields = ('id', 'title', 'code', 'linenos', 'language', 'style') 7. 通過(guò)Serializer編寫Django View
讓我們來(lái)看一下,如何通過(guò)我們創(chuàng)建的serializer類編寫django view。這里我們不使用rest framework的其他特性,僅編寫正常的django view。
我們創(chuàng)建一個(gè)HttpResponse 子類,這樣我們可以將我們返回的任何數(shù)據(jù)轉(zhuǎn)換成json。
在snippet/views.py中添加以下內(nèi)容:
from django.http import HttpResponsefrom django.views.decorators.csrf import csrf_exemptfrom rest_framework.renderers import JSONRendererfrom rest_framework.parsers import JSONParserfrom snippets.models import Snippetfrom snippets.serializers import SnippetSerializerclass JSONResponse(HttpResponse): """ An HttpResponse that renders it's content into JSON. """ def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs)
我們API的目的是,可以通過(guò)view來(lái)列舉全部的Snippet的內(nèi)容,或者創(chuàng)建一個(gè)新的snippet
@csrf_exemptdef snippet_list(request): """ List all code snippets, or create a new snippet. """ if request.method == 'GET': snippets = Snippet.objects.all() serializer = SnippetSerializer(snippets) 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) else: return JSONResponse(serializer.errors, status=400)
注意,因?yàn)槲覀円ㄟ^(guò)client向該view post一個(gè)請(qǐng)求,所以我們要將該view 標(biāo)注為csrf_exempt, 以說(shuō)明不是一個(gè)CSRF事件。
Note that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as csrf_exempt. This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now.
我們也需要一個(gè)view來(lái)操作一個(gè)單獨(dú)的Snippet,以便能更新/刪除該對(duì)象。
@csrf_exemptdef 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) else: return JSONResponse(serializer.errors, status=400) elif request.method == 'DELETE': snippet.delete() return HttpResponse(status=204)
將views.py保存,在Snippets目錄下面創(chuàng)建urls.py,添加以下內(nèi)容:
urlpatterns = patterns('snippets.views', url(r'^snippets/$', 'snippet_list'), url(r'^snippets/(?P<pk>[0-9]+)/$', 'snippet_detail'),) 注意我們有些邊緣事件沒有處理,服務(wù)器可能會(huì)拋出500異常。
8. 測(cè)試
現(xiàn)在我們啟動(dòng)server來(lái)測(cè)試我們的Snippet。
在python mange.py shell終端下執(zhí)行(如果前面進(jìn)入還沒有退出)
>>quit()
執(zhí)行下面的命令, 運(yùn)行我們的server:
python manage.py runserverValidating models...0 errors foundDjango version 1.4.3, using settings 'tutorial.settings'Development server is running at http://127.0.0.1:8000/Quit the server with CONTROL-C.
新開一個(gè)terminal來(lái)測(cè)試我們的server
序列化:
url http://127.0.0.1:8000/snippets/[{"id": 1, "title": "", "code": "print /"hello, world/"/n", "linenos": false, "language": "python", "style": "friendly"}] url http://127.0.0.1:8000/snippets/1/{"id": 1, "title": "", "code": "print /"hello, world/"/n", "linenos": false, "language": "python", "style": "friendly"}
Request and Response
1. Request Object ——Request對(duì)象
rest framework 引入了一個(gè)繼承自HttpRequest的Request對(duì)象,該對(duì)象提供了對(duì)請(qǐng)求的更靈活解析。request對(duì)象的核心部分是request.data屬性,類似于request.post, 但在使用WEB API時(shí),request.data更有效。
(1)request.POST # Only handles form data. Only works for 'POST' method.
(2)request.DATA # Handles arbitrary data. Works any HTTP request with content.
2. Response Object ——Response對(duì)象
rest framework引入了一個(gè)Response 對(duì)象,它繼承自TemplateResponse對(duì)象。它獲得未渲染的內(nèi)容并通過(guò)內(nèi)容協(xié)商content negotiation 來(lái)決定正確的content type返回給client。
return Response(data) # Renders to content type as requested by the client.
3. Status Codes
在views當(dāng)中使用數(shù)字化的HTTP狀態(tài)碼,會(huì)使你的代碼不宜閱讀,且不容易發(fā)現(xiàn)代碼中的錯(cuò)誤。rest framework為每個(gè)狀態(tài)碼提供了更明確的標(biāo)識(shí)。例如HTTP_400_BAD_REQUEST。相比于使用數(shù)字,在整個(gè)views中使用這類標(biāo)識(shí)符將更好。
4. 封裝API views
在編寫API views時(shí),REST Framework提供了兩種wrappers:
1). @api_viwe 裝飾器 ——函數(shù)級(jí)別
2). APIView 類——類級(jí)別
這兩種封裝器提供了許多功能,例如,確保在view當(dāng)中能夠接收到Request實(shí)例;往Response中增加內(nèi)容以便內(nèi)容協(xié)商content negotiation 機(jī)制能夠執(zhí)行。
封裝器也提供一些行為,例如在適當(dāng)?shù)臅r(shí)候返回405 Methord Not Allowed響應(yīng);在訪問多類型的輸入request.DATA時(shí),處理任何的ParseError異常。
5. 匯總
我們開始用這些新的組件來(lái)寫一些views。
我們不在需要JESONResponse 類(在前一篇中創(chuàng)建),將它刪除。刪除后我們開始稍微重構(gòu)下我們的view
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 snippets, or create a new snippet. """ if request.method == 'GET': snippets = Snippet.objects.all() serializer = SnippetSerializer(snippets) 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) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
上面的代碼是對(duì)我們之前代碼的改進(jìn)。看上去更簡(jiǎn)潔,也更類似于django的forms api形式。我們也采用了狀態(tài)碼,使返回值更加明確。
下面是對(duì)單個(gè)snippet操作的view更新:
@api_view(['GET', 'PUT', 'DELETE'])def snippet_detail(request, pk): """ Retrieve, update or delete a snippet instance. """ 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) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': snippet.delete() return Response(status=status.HTTP_204_NO_CONTENT)
注意,我們并沒有明確的要求requests或者responses給出content type。request.DATA可以處理輸入的json請(qǐng)求,也可以輸入yaml和其他格式。類似的在response返回?cái)?shù)據(jù)時(shí),REST Framework返回正確的content type給client。
6. 給URLs增加可選的格式后綴
利用在response時(shí)不需要指定content type這一事實(shí),我們?cè)贏PI端增加格式的后綴。使用格式后綴,可以明確的指出使用某種格式,意味著我們的API可以處理類似http://example.com/api/items/4.json.的URL。
增加format參數(shù)在views中,如:
def snippet_list(request, format=None):anddef snippet_detail(request, pk, format=None):
現(xiàn)在稍微改動(dòng)urls.py文件,在現(xiàn)有的URLs中添加一個(gè)格式后綴pattterns (format_suffix_patterns):
from django.conf.urls import patterns, urlfrom rest_framework.urlpatterns import format_suffix_patternsurlpatterns = patterns('snippets.views', url(r'^snippets/$', 'snippet_list'), url(r'^snippets/(?P<pk>[0-9]+)$', 'snippet_detail'),)urlpatterns = format_suffix_patterns(urlpatterns) 這些額外的url patterns并不是必須的。



















