django Rest Framework 系列 1
$mkdir ~/env
$virtualenv ~/env/tutorial
$source ~/env/tutorial/bin/avtivate
現在我們處在一個虛擬的環境中,開始安裝我們的依賴包
$pip install django
$pip install djangorestframework
$pip install pygments ////使用這個包,做程式碼高亮顯示
需要退出虛擬環境時,執行deactivate。更多資訊,irtualenv document
2 開始
環境準備好只好,我們開始建立我們的專案
$ cd ~
$ django-admin.py startproject tutorial
$ cd tutorial
專案建立好後,我們再建立一個簡單的app
$Python manage.py startapp snippets
我們使用sqlite3來執行我們的專案tutorial,編輯tutorial/settings.py, 將資料庫的預設引擎engine改為sqlite3, 資料庫的名字NAME改為tmp.db
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'tmp.db',
'USER': '',
'PASSWORD' : '',
'HOST': '',
'PORT': '',
}
}
同時更改settings.py檔案中的INSTALLD_APPS,新增我們的APP snippets和rest_framework
INSTALLED_APPS = (
...
'rest_framework',
'snippets',
)
在tutorial/urls.py中,將snippets app的url包含進來
urlpatterns = patterns('',
url(r'^', include('snippets.urls' )),
)
3 建立Model
這裡我們建立一個簡單的nippets model,目的是用來儲存程式碼片段
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, 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',)
完成model時,記得sync下資料庫
python manage.py syncdb
4 . 建立序列化類
我們要使用我們的web api,要做的第一件事就是序列化和反序列化, 以便snippets例項能轉換為可表述的內容,例如json. 我們宣告一個可有效工作的序列器serializer。在snippets目錄下面,該序列器與django 的表單形式很類似。建立一個serializers.py ,並將下面內容拷貝到檔案中.
from django.forms import widgets
from rest_framework import serializers
from snippets.models import Snippet
class SnippetSerializer(serializers.Serializer):
pk = serializers.Field() # Note: `Field` is an untyped read-only 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=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 方法定義瞭如何通過反序列化資料,生成正確的物件例項
我們也可以使用ModelSerializer來快速生成,後面我們將介紹如何使用它。
5. 使用 Serializers
在我們使用我們定義的SnippetsSerializers之前,我們先熟悉下Snippets.
$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='print "hello, world"\n')
snippet.save()
我們現在獲得了一個Snippets的例項,現在我們對他進行以下序列化
serializer = SnippetSerializer(snippet)
serializer.data
# {'pk': 1, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
這時,我們將該例項轉成了python原生的資料型別。下面我們將該資料轉換成json格式,以完成序列化:
content = JSONRenderer().render(serializer.data)
content
# '{"pk": 1, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
反序列化也很簡單,首先我們要將一個輸入流(content),轉換成python的原生資料型別
from io import StringIO
stream = StringIO(content)
data = JSONParser().parse(stream)
然後我們將該原生資料型別,轉換成物件例項
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.object
# <Snippet: Snippet object>
注意這些API和django表單的相似處。這些相似點, 在我們講述在view中使用serializers時將更加明顯。
6. 使用 ModelSerializers
SnippetSerializer使用了許多和Snippet中相同的程式碼。如果我們能把這部分程式碼去掉,看上去將更佳簡潔。
類似與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. 通過Serializer編寫Django View
讓我們來看一下,如何通過我們建立的serializer類編寫django view。這裡我們不使用rest framework的其他特性,僅編寫正常的django view。
我們建立一個HttpResponse 子類,這樣我們可以將我們返回的任何資料轉換成json。
在snippet/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):
"""
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)
# 在django1.9+版本中直接喲JSONResponse的方法
我們API的目的是,可以通過view來列舉全部的Snippet的內容,或者建立一個新的snippet
@csrf_exempt
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
# 對於返回的是QuerySet結果集,序列化的時候要設定引數many=True,如果只是一個單例項object,可以不用,預設False
serializer = SnippetSerializer(snippets,many=True)
# JSONResponse預設引數值為dict,如果是非dict,則需要引數safe=False
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)
else:
return JSONResponse(serializer.errors, status=400)
注意,因為我們需要從一個沒有獲取CSRF token的客戶端來POST這個檢視,所以我們將該檢視標記為scrf_exempt,但平時你並不需要這麼做,針對這種情況REST框架會有更合理的特性,但為了達到預期的目的我們暫時先這麼做。
我們也需要一個view來操作一個單獨的Snippet,以便能更新/刪除該物件。
@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)
else:
return JSONResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
snippet.delete()
return HttpResponse(status=204)
將views.py儲存,在Snippets目錄下面建立urls.py,新增以下內容:
urlpatterns = patterns('snippets.views',
url(r'^snippets/$', 'snippet_list'),
url(r'^snippets/(?P<pk>[0-9]+)/$', 'snippet_detail'),
)
注意我們有些邊緣事件沒有處理,伺服器可能會丟擲500異常。
8. 測試
現在我們啟動server來測試我們的Snippet。
執行下面的命令, 執行我們的server:
$ python manage.py runserver
Validating models...
0 errors found
Django 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.
新開一個terminal來測試我們的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"}