Serialization——序列化
前言
本教程將會介紹建立一個簡單webAPI。在整個學習的過程中,將會逐一介紹到REST framework的各種元件,讓你全面的瞭解所有東西是如果組合在一起的。
這個教程將會相當的詳細,所以在開始之前,你應該去來點餅乾,再喝上一些你喜歡的飲料。如果你只是想快速概覽,你應該檢視Quickstart.
注意:本教程的程式碼可以在GitHub的 tomchristie/rest-framework-tutorial中找到。測試,這是一個測試連結。
建立一個新的虛擬環境
使用 virtualenv
建立一個虛擬環境.
virtualenv env
source env/bin/activate
然後我們在我們建立的虛擬環境中安裝需要的包。
pip install django
pip install djangorestframework
pip install pygments #使用它來程式碼高亮
注意:使用 deactivate
來隨時退出虛擬環境。更多的資訊請檢視Virtualenv documentation
準備
我們這邊開始編寫程式碼了。先來建立一個專案吧~
cd ~
django-admin.py startproject tutorial
cd tutorial
接下來,我們建立一個app。
python manage.py startapp snippets
我們需要新增 snippets
和 rest_framework
到 INSTALLED_APPS
,在 tutorial/settings.py
檔案中:
INSTALLED_APPS = {
...
`rest_framework`,
`snippets.apps.SnippetsConfig`,
}
注意:如果你使用的django<1.9,你需要替換 snippets.apps.SnippetsConfig
為snippets
。
我們可以繼續上路了。
建立model
在本教程中,我們將建立一個簡單的model,這個model將用於儲存程式碼片段。
編輯 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,defalut=``)
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`)
執行命令,建立資料表:
python manage.py makemigrations snippets
python manage.py migrate
建立Serializer
首先,我們需要給我們的web API 提供一種序列化和反序列化的呈現方式,如json
。
我們可以申明一個和django的forms差不多的serializers。建立一個Serializers.py
檔案。
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.Serializer):
id = 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):
return Snippet.objects.create(**validated_data)
def update(self,instance,validated_data):
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的第一步是定義需要 序列化/反序列化 的欄位。create()
和update()
方法定義當serializer.save()
時,如果建立和更新。
serializer類非常類似於Django的Form
類。包括各個欄位的驗證標記,例如 required
,max_length
,default
。
欄位標記也能夠控制serializer在某些環境中如何顯示,例如呈現為HTML.
之前的{`base_template`: `textarea.html`}標記相當於django
Form中的
widget=widgets.Textarea`。
其實我們使用ModelSerializer
類更能夠節省我們的時間,但現在,我們還是顯示的定義我們的欄位。
開始工作
在我們更進一步學習之前,我們將通過django shell
來熟悉serializer。
python manage.py shell
我們需要匯入我們需要的東西,然後建立兩個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"
`)
snippet.save()
snippet = Snippet(code=`print "hello, world"
`)
snippet.save()
我們來看看我們建立的其中一個例項:
serializer = SnippetSerializer(snippet)
serializer.data
# {`id`: 2, `title`: u``, `code`: u`print "hello, world"
`, `linenos`: False, `language`: u`python`, `style`: u`friendly`}
在這裡,我們翻譯這個model例項為python原生的資料型別,為了完成序列化,我們將資料呈現為JSON。
content = JSONRenderer().render(serializer.data)
content
# `{"id": 2, "title": "", "code": "print \"hello, world\"\n", "linenos": false, "language": "python", "style": "friendly"}`
反序列化類似。首先,我們解析一個流為Python原生資料型別。
from django.utils.six import BytesIO
data = JSONParser().parse(stream)
然後我們還原原生資料型別到一個完全填充的物件例項中。
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([(`title`, ``), (`code`, `print "hello, world"
`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)])
serializer.save()
# <Snippet: Snippet object>
注意:這類似於forms.當我們編寫views使用serializer時,這會看起來更相似。
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([(`id`, 1), (`title`, u``), (`code`, u`foo = "bar"
`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)]), OrderedDict([(`id`, 2), (`title`, u``), (`code`, u`print "hello, world"
`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)]), OrderedDict([(`id`, 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
來重構我們的serializer類。開啟snippets/serializers.py
,替換SinppetSerializer
類:
class SnippetSerializer(serializers.ModelSerizer):
class Meta:
model = Snippet
fields = (`id`,`title`,`code`,`linenos`,`language`,`style`)
serializer擁有一個很好的屬性,你能夠通過列印檢視一個serializer例項的所有欄位。開啟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`)...
注意:ModelSerializer
不會做任何如魔法般的事情,它只是更快捷的建立serializer
- 自動的確定欄位的設定
- 簡單的預設
create()
和update()
方法
使用Serializer寫一個常規的django views
讓我們使用我們新的serializer來寫一些API檢視吧。在這裡,我們不會使用任何REST framework的其他特性,我們就寫一個常規的 django views。
來編寫 snippets/views.py
檔案吧:
from django.http import HttpResponse, JsonResponse
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
我們的API將會支援檢視所有資料和建立一個新的snippet
@csrf_exempt
def snippet_list(request):
"""
顯示所有的程式碼片段,或者建立一個新的片段
"""
if request.method == `GET`:
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JsonResponse(serializer.data, safe=False)
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 views會更加明智的做這個事情。但是現在,他可以使用。
我們還需要這個檢視能夠檢索,更新和刪除。
@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)
最後,我們需要建立url,在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),
]
我們還需要在跟urls.py(tutorial/urls.py
)上引入我們的urls.py
from django.conf.urls import url, include
urlpatterns = [
url(r`^`, include(`snippets.urls`)),
]
注意:我們的檢視有某些問題是還沒有做妥善的處理,比如傳送的json格式不正確,或是呼叫檢視沒有的方法,那麼我們就會返回一個500“伺服器錯誤”的訊息。