我覺得官方文檔十分不錯(cuò)赂韵,比我搜到的教程要靠譜送淆,按照教程敲一遍,再仔細(xì)琢磨琢磨摊灭,感覺懂了不少咆贬。
教程1:序列化
1. 1 介紹:
本教程將介紹如何創(chuàng)建一個(gè)簡(jiǎn)單的粘貼代碼,突出顯示W(wǎng)eb API斟或。
一路上素征,將介紹組成REST框架的各種組件,并全面了解一切如何融合在一起萝挤。
該教程是相當(dāng)深入的御毅,所以你應(yīng)該在開始之前獲得一個(gè)cookie和一杯你最喜歡的釀造。
如果您只想快速瀏覽怜珍,請(qǐng)改用快速入門文檔端蛆。
注意:本教程的代碼可在GitHub的tomchristie / rest-framework-tutorial存儲(chǔ)庫(kù)中可以找到。
完成的實(shí)施也是在線作為沙盒版本進(jìn)行測(cè)試酥泛,這里可以看到今豆。
1.2 建立一個(gè)新的環(huán)境
在我們做任何事情之前,我們將使用virtualenv創(chuàng)建一個(gè)新的虛擬環(huán)境柔袁。這將確保我們的包配置與我們正在開展的任何其他項(xiàng)目保持良好的隔離呆躲。
PS:其實(shí)Python3.6可以用自帶的venv。
virtualenv env
source env/bin/activate
現(xiàn)在我們?cè)谝粋€(gè)virtualenv環(huán)境中捶索,我們可以安裝我們的包的要求插掂。
pip install django
pip install djangorestframework
pip install pygments
PS:pygments庫(kù)提供代碼高亮
注意:要隨時(shí)退出virtualenv環(huán)境,只需鍵入deactivate腥例。有關(guān)更多信息辅甥,請(qǐng)參閱 virtualenv文檔 。
1.3 入門:
好的燎竖,我們準(zhǔn)備好獲得代碼璃弄。要開始,我們先來創(chuàng)建一個(gè)新的項(xiàng)目构回。
cd ~
django-admin.py startproject tutorial
cd tutorial
一旦完成夏块,我們可以創(chuàng)建一個(gè)我們將用來創(chuàng)建一個(gè)簡(jiǎn)單的Web API的應(yīng)用程序。
python manage.py startapp snippets
我們需要添加我們的新snippets應(yīng)用和rest_framework應(yīng)用INSTALLED_APPS捐凭。我們來編輯tutorial/settings.py文件:
INSTALLED_APPS = (
...
'rest_framework',
'snippets.apps.SnippetsConfig',
)
請(qǐng)注意拨扶,如果你使用的Django <1.9,則需要更換snippets.apps.SnippetsConfig有snippets茁肠。
好的,我們準(zhǔn)備好了缩举。
1.4 創(chuàng)建一個(gè)可以使用的模型
為了本教程的目的匹颤,我們將首先創(chuàng)建一個(gè)Snippet用于存儲(chǔ)代碼片段的簡(jiǎn)單模型托猩。繼續(xù)編輯snippets/models.py文件。注意:良好的編程實(shí)踐包括評(píng)論京腥。雖然您將在本教程代碼的存儲(chǔ)庫(kù)版本中找到它們赦肃,但我們?cè)诖撕雎粤怂鼈儯瑢W⒂诖a本身他宛。
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',)
我們還需要為我們的代碼段模型創(chuàng)建初始遷移欠气,并首次同步數(shù)據(jù)庫(kù)厅各。
python manage.py makemigrations snippets
python manage.py migrate
1.5 創(chuàng)建一個(gè)Serializer類
我們需要開始使用Web API的第一件事是提供一種將代碼片段實(shí)例序列化和反序列化為表示形式的方法json。我們可以通過聲明與Django表單非常相似的序列化器來做到這一點(diǎn)预柒。在snippets命名的目錄中創(chuàng)建一個(gè)文件,serializers.py并添加以下內(nèi)容宜鸯。
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):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the 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
序列化器類的第一部分定義了序列化/反序列化的字段淋袖。該create()和update()方法定義實(shí)例如何完全成熟的創(chuàng)建或修改時(shí)調(diào)用serializer.save()
甲串行類非常類似于一個(gè)Django Form類,并且包括關(guān)于各個(gè)字段類似的驗(yàn)證標(biāo)記适贸,如required,max_length和default拜姿。
字段標(biāo)志還可以控制在某些情況下蕊肥,如渲染到HTML時(shí)如何顯示串行器。{'base_template': 'textarea.html'}上面的標(biāo)志相當(dāng)于widget=widgets.Textarea在Django Form類上使用壁却。這對(duì)于控制如何顯示可瀏覽的API特別有用,我們將在本教程的后面看到赔硫。
我們實(shí)際上也可以通過使用ModelSerializer課程來節(jié)省自己的時(shí)間盐肃,我們稍后會(huì)看到权悟,但是現(xiàn)在我們將保持我們的序列化器定義推盛。
1.6 使用serializers
在我們進(jìn)一步了解之前,我們將熟悉使用我們新的Serializer類耘成。我們進(jìn)入Django shell。
python manage.py shell
好的瘪菌,一旦進(jìn)入我們有幾個(gè)庫(kù)要導(dǎo)入撒会,我們來創(chuàng)建一些代碼片段來處理。
注意:以下部分在shell里面編寫控嗜,需要一行一行寫,這樣才能看到結(jié)果:
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()
我們現(xiàn)在有幾個(gè)片段實(shí)例可以玩曾掂。我們來看看序列化這些實(shí)例之一壁顶。
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
此時(shí),我們將模型實(shí)例轉(zhuǎn)換為Python本機(jī)數(shù)據(jù)類型许蓖。為了完成序列化過程调衰,我們將數(shù)據(jù)轉(zhuǎn)換成json。
content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
反序列化是類似的嚎莉。首先我們將一個(gè)流解析為Python本機(jī)數(shù)據(jù)類型...
from django.utils.six import BytesIO
stream = BytesIO(content)
data = JSONParser().parse(stream)
然后我們將這些本機(jī)數(shù)據(jù)類型恢復(fù)到完全填充的對(duì)象實(shí)例中趋箩。
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>
請(qǐng)注意API與表單的使用情況。當(dāng)我們開始編寫使用我們的串行器的視圖時(shí)跳芳,相似性將變得更加明顯竹勉。
我們也可以序列化查詢集而不是模型實(shí)例。為此,我們只many=True需要為serializer參數(shù)添加一個(gè)標(biāo)志车胡。
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
注意:下方的代碼為代碼優(yōu)化說明照瘾,并不代表要寫丧慈!
1.7 使用ModelSerializers
我們的SnippetSerializer類正在復(fù)制Snippet模型中還包含的大量信息逃默。如果我們可以保持我們的代碼更簡(jiǎn)潔,那將是很好的完域。
與Django提供Form類和ModelForm類的方式相同,REST框架包括Serializer類和ModelSerializer類凹耙。
我們來看看使用ModelSerializer類重構(gòu)我們的serializer 肠仪。snippets/serializers.py再次打開該文件,并用SnippetSerializer以下替換該類意述。
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
序列化器具有的一個(gè)不錯(cuò)的屬性是可以通過打印其表示來檢查序列化器實(shí)例中的所有字段吮蛹。打開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類不會(huì)做任何特別神奇的事情然低,它們只是創(chuàng)建序列化器類的快捷方式:
- 一組自動(dòng)確定的字段。
- 簡(jiǎn)單的默認(rèn)實(shí)現(xiàn)create()和update()方法带兜。
1.8 使用我們的Serializer編寫正常的Django視圖
我們來看看我們?nèi)绾问褂梦覀兊男碌腟erializer類編寫一些API視圖吨灭。目前我們不會(huì)使用任何REST框架的其他功能,我們只需將視圖編寫為常規(guī)的Django視圖无畔。
編輯snippets/views.py文件,并添加以下內(nèi)容恭理。
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的根本將是一個(gè)視圖郭变,支持列出所有現(xiàn)有的片段,或創(chuàng)建一個(gè)新的片段诉濒。
@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, 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)
請(qǐng)注意未荒,因?yàn)槲覀兿M軌驈牟痪哂蠧SRF令牌的客戶端對(duì)此視圖進(jìn)行POST,因此我們需要將視圖標(biāo)記為csrf_exempt寨腔。這不是你通常想要做的事情划纽,REST框架視圖實(shí)際上比這更有明確的行為,但它現(xiàn)在將用于我們的目的勇劣。
我們還需要一個(gè)與單個(gè)代碼段對(duì)應(yīng)的視圖,并可用于檢索幻捏,更新或刪除代碼段命咐。
@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)
最后我們需要把這些節(jié)點(diǎn)連接起來醋奠。創(chuàng)建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文件中連接根urlconf ,以包含我們的片段應(yīng)用程序的URL沛善。
from django.conf.urls import url, include
urlpatterns = [
url(r'^', include('snippets.urls')),
]
值得注意的是塞祈,我們目前還沒有正確處理的幾個(gè)邊緣案例。如果我們發(fā)送格式錯(cuò)誤json,或者如果請(qǐng)求是使用視圖不處理的方法媳友,那么我們最終會(huì)出現(xiàn)500個(gè)“服務(wù)器錯(cuò)誤”響應(yīng)产捞。不過,現(xiàn)在這樣做搂抒。
1.9 測(cè)試我們?cè)赪eb API上的第一次嘗試
現(xiàn)在我們可以啟動(dòng)一個(gè)服務(wù)我們的代碼片段的示例服務(wù)器尿扯。
退出shell...
quit()
并啟動(dòng)Django的開發(fā)服務(wù)器焰雕。
python manage.py runserver
在另一個(gè)終端窗口中,我們可以測(cè)試服務(wù)器辟宗。
我們可以使用
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"
}
同樣约郁,您可以通過在Web瀏覽器中訪問這些URL來顯示相同??的json。
---end---