我覺得官方文檔十分不錯,比我搜到的教程要靠譜,按照教程敲一遍,再仔細琢磨琢磨,感覺懂了不少。
教程1:序列化
1. 1 介紹:
本教程將介紹如何創建一個簡單的粘貼代碼,突出顯示Web API。
一路上,將介紹組成REST框架的各種組件,并全面了解一切如何融合在一起。
該教程是相當深入的,所以你應該在開始之前獲得一個cookie和一杯你最喜歡的釀造。
如果您只想快速瀏覽,請改用快速入門文檔。
注意:本教程的代碼可在GitHub的tomchristie / rest-framework-tutorial存儲庫中可以找到。
完成的實施也是在線作為沙盒版本進行測試,這里可以看到。
1.2 建立一個新的環境
在我們做任何事情之前,我們將使用virtualenv創建一個新的虛擬環境。這將確保我們的包配置與我們正在開展的任何其他項目保持良好的隔離。
PS:其實Python3.6可以用自帶的venv。
virtualenv env
source env/bin/activate
現在我們在一個virtualenv環境中,我們可以安裝我們的包的要求。
pip install django
pip install djangorestframework
pip install pygments
PS:pygments庫提供代碼高亮
注意:要隨時退出virtualenv環境,只需鍵入deactivate。有關更多信息,請參閱 virtualenv文檔 。
1.3 入門:
好的,我們準備好獲得代碼。要開始,我們先來創建一個新的項目。
cd ~
django-admin.py startproject tutorial
cd tutorial
一旦完成,我們可以創建一個我們將用來創建一個簡單的Web API的應用程序。
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。
好的,我們準備好了。
1.4 創建一個可以使用的模型
為了本教程的目的,我們將首先創建一個Snippet用于存儲代碼片段的簡單模型。繼續編輯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',)
我們還需要為我們的代碼段模型創建初始遷移,并首次同步數據庫。
python manage.py makemigrations snippets
python manage.py migrate
1.5 創建一個Serializer類
我們需要開始使用Web API的第一件事是提供一種將代碼片段實例序列化和反序列化為表示形式的方法json。我們可以通過聲明與Django表單非常相似的序列化器來做到這一點。在snippets命名的目錄中創建一個文件,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):
"""
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()方法定義實例如何完全成熟的創建或修改時調用serializer.save()
甲串行類非常類似于一個Django Form類,并且包括關于各個字段類似的驗證標記,如required,max_length和default。
字段標志還可以控制在某些情況下,如渲染到HTML時如何顯示串行器。{'base_template': 'textarea.html'}上面的標志相當于widget=widgets.Textarea在Django Form類上使用。這對于控制如何顯示可瀏覽的API特別有用,我們將在本教程的后面看到。
我們實際上也可以通過使用ModelSerializer課程來節省自己的時間,我們稍后會看到,但是現在我們將保持我們的序列化器定義。
1.6 使用serializers
在我們進一步了解之前,我們將熟悉使用我們新的Serializer類。我們進入Django shell。
python manage.py shell
好的,一旦進入我們有幾個庫要導入,我們來創建一些代碼片段來處理。
注意:以下部分在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
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
此時,我們將模型實例轉換為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
stream = BytesIO(content)
data = JSONParser().parse(stream)
然后我們將這些本機數據類型恢復到完全填充的對象實例中。
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與表單的使用情況。當我們開始編寫使用我們的串行器的視圖時,相似性將變得更加明顯。
我們也可以序列化查詢集而不是模型實例。為此,我們只many=True需要為serializer參數添加一個標志。
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')])]
注意:下方的代碼為代碼優化說明,并不代表要寫!
1.7 使用ModelSerializers
我們的SnippetSerializer類正在復制Snippet模型中還包含的大量信息。如果我們可以保持我們的代碼更簡潔,那將是很好的。
與Django提供Form類和ModelForm類的方式相同,REST框架包括Serializer類和ModelSerializer類。
我們來看看使用ModelSerializer類重構我們的serializer 。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')...
重要的是要記住,ModelSerializer類不會做任何特別神奇的事情,它們只是創建序列化器類的快捷方式:
- 一組自動確定的字段。
- 簡單的默認實現create()和update()方法。
1.8 使用我們的Serializer編寫正常的Django視圖
我們來看看我們如何使用我們的新的Serializer類編寫一些API視圖。目前我們不會使用任何REST框架的其他功能,我們只需將視圖編寫為常規的Django視圖。
編輯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的根本將是一個視圖,支持列出所有現有的片段,或創建一個新的片段。
@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)
請注意,因為我們希望能夠從不具有CSRF令牌的客戶端對此視圖進行POST,因此我們需要將視圖標記為csrf_exempt。這不是你通常想要做的事情,REST框架視圖實際上比這更有明確的行為,但它現在將用于我們的目的。
我們還需要一個與單個代碼段對應的視圖,并可用于檢索,更新或刪除代碼段。
@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文件中連接根urlconf ,以包含我們的片段應用程序的URL。
from django.conf.urls import url, include
urlpatterns = [
url(r'^', include('snippets.urls')),
]
值得注意的是,我們目前還沒有正確處理的幾個邊緣案例。如果我們發送格式錯誤json,或者如果請求是使用視圖不處理的方法,那么我們最終會出現500個“服務器錯誤”響應。不過,現在這樣做。
1.9 測試我們在Web API上的第一次嘗試
現在我們可以啟動一個服務我們的代碼片段的示例服務器。
退出shell...
quit()
并啟動Django的開發服務器。
python manage.py runserver
在另一個終端窗口中,我們可以測試服務器。
我們可以使用
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---