該教程將會讓你理解 REST 框架的各個組件是怎麼工做的。html
該教程很深刻,你可能須要取餅乾和你喜歡的飲料。若是你想快速的瀏覽,你應該去看 quickstart 。python
注意:該教程對應的代碼能夠去GitHub看, tomchristie/rest-framework-tutorial 。完整的實現做爲測試版本放在這裏:available here.git
在咱們作以前,須要使用virtualenv創建一個新的虛擬環境。這將確保咱們的包配置無缺地與其餘工程相隔離。github
virtualenv env source env/bin/activate
如今咱們在一個虛擬的環境,咱們能夠安裝須要的包。web
pip install django pip install djangorestframework pip install pygments # We'll be using this for the code highlighting 咱們使用它讓代碼高亮。
注意:須要退出虛擬環境,只需鍵入deactivate。更多信息能夠看這裏:
virtualenv documentationsql
好了,咱們準備編寫代碼,創建一個新的工程shell
cd ~ django-admin.py startproject tutorial cd tutorial
一旦創建了工程,咱們就要創建一個app用來創建一個簡單的Web API。數據庫
python manage.py startapp snippets
The simplest way to get up and running will probably be to use an sqlite3
database for the tutorial. Edit the tutorial/settings.py
file, and set the default database "ENGINE"
to "sqlite3"
, and "NAME"
to "tmp.db"
.django
要使用一個sqlite3數據庫,最簡單的方式就是在 tutorial/settings.py 文件中配置以下:json
DATABASES ={'default':{'ENGINE':'django.db.backends.sqlite3','NAME':'tmp.db','USER':'','PASSWORD':'','HOST':'','PORT':'',}}
We'll also need to add our new snippets
app and the rest_framework
app to INSTALLED_APPS
.
咱們還須要把 rest_framework 和在上面創建的新 app 添加進來。
INSTALLED_APPS =(...'rest_framework','snippets',)
We also need to wire up the root urlconf, in the tutorial/urls.py
file, to include our snippet app's URLs.
一樣須要配置根 URL , 編輯 tutorial/urls.py 文件,把 snippet 的 URL 包含進來。
urlpatterns = patterns('', url(r'^', include('snippets.urls')),)
For the purposes of this tutorial we're going to start by creating a simple Snippet
model that is used to store code snippets. Go ahead and edit the snippets
app's models.py
file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself.
爲 app snippet 創建一個簡單的數據庫模型名爲 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)
classMeta: ordering =('created',)
Don't forget to sync the database for the first time.
別忘了同步數據庫。
python manage.py syncdb
The first thing we need to get started on our Web API is provide a way of serializing and deserializing the snippet instances into representations such as json
. We can do this by declaring serializers that work very similar to Django's forms. Create a file in the snippets
directory named serializers.py
and add the following.
咱們須要爲 Web API 提供序列化和逆序列化方法,用像 json 這樣的數據格式來表現 snippet 數據模型。咱們能夠像Django的表單類那樣來聲明序列化器類。在 app snippets 目錄下創建一個名爲 serializers.py 的文件,在該文件中鍵入:
from django.forms import widgets from rest_framework import serializers from snippets.models importSnippet, LANGUAGE_CHOICES, STYLE_CHOICES 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=LANGUAGE_CHOICES, default='python') style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
def restore_object(self, attrs, instance=None):
""" Create or update a new snippet instance, given a dictionary of deserialized field values.
建立或者更新一個新的 snippet 實例,須要一個字典,該字典包含逆序列化域的值。 Note that if we don't define this method, then deserializing data will simply return a dictionary of items.
若是咱們沒有定義改方法,納秒逆序列化數據只會簡單地返回一個項目字典 """
if instance:# Update existing instance instance.title = attrs.get('title', instance.title) instance.code = attrs.get('code', instance.code) instance.linenos = attrs.get('linenos', instance.linenos) instance.language = attrs.get('language', instance.language) instance.style = attrs.get('style', instance.style)
return instance # Create new instancereturnSnippet(**attrs)
The first part of serializer class defines the fields that get serialized/deserialized. The restore_object
method defines how fully fledged instances get created when deserializing data.
該序列化器類的第一部分定義了須要序列化和逆序列化的域。restore_object方法定義了怎樣用逆序列化的數據來建立一個新的實例。
Notice that we can also use various attributes that would typically be used on form fields, such as widget=widgets.Textarea
. These can be used to control how the serializer should render when displayed as an HTML form. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.
注意:咱們一樣可使用其餘更多的用在表單中的屬性,像 widget=weidgets.Textarea。這些屬性能夠用來控制序列化器渲染一個 HTML 表單的行爲,尤爲對顯示可瀏覽的 API 有用,這個咱們會在後面的教程中看到。
We can actually also save ourselves some time by using the ModelSerializer
class, as we'll see later, but for now we'll keep our serializer definition explicit.
咱們能夠用ModelSerializer類來快速生成,可是如今咱們會顯示地定義序列化器。
Before we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell.
在進行進一步的工做以前,咱們先熟悉咱們的新的序列化器類。在shell鍵入:
python manage.py shell
Okay, once we've got a few imports out of the way, let's create a couple of code snippets to work with.
建立一個snippets實例:
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()
We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances.
咱們得到一些 snippet 實例,看看序列化其中一個的效果:
serializer =SnippetSerializer(snippet) serializer.data # {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
At this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into json
.
上面咱們將 模型的實例轉換成Python數據類型。咱們用序列化器將其渲染成 json:
content =JSONRenderer().render(serializer.data) content # '{"pk": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
Deserialization is similar. First we parse a stream into Python native datatypes...
逆序列化是相似的,首先咱們解析數據流,將其轉成Python數據類型。
# This import will use either `StringIO.StringIO` or `io.BytesIO`
# as appropriate, depending on if we're running Python 2 or Python 3.
from rest_framework.compat importBytesIO stream =BytesIO(content) data =JSONParser().parse(stream)
...then we restore those native datatypes into to a fully populated object instance.
而後存儲Python數據類型到對象實例中去。
serializer =SnippetSerializer(data=data) serializer.is_valid()# True serializer.object # <Snippet: Snippet object>
Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.
注意這些API和django表單的類似處。這些類似點, 在咱們講述在view中使用serializers時將更加明顯。
We can also serialize querysets instead of model instances. To do so we simply add a many=True
flag to the serializer arguments.
我麼也能夠序列化 querysets,只須要簡單的加上 many = True。
serializer =SnippetSerializer(Snippet.objects.all(), many=True) serializer.data # [{'pk': 1, 'title': u'', 'code': u'foo = "bar"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}, {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}]
Our SnippetSerializer
class is replicating a lot of information that's also contained in the Snippet
model. It would be nice if we could keep our code a bit more concise.
In the same way that Django provides both Form
classes and ModelForm
classes, REST framework includes both Serializer
classes, and ModelSerializer
classes.
Let's look at refactoring our serializer using the ModelSerializer
class. Open the file snippets/serializers.py
again, and edit the SnippetSerializer
class.
上面的 SnipperSerializer 類有不少在Snippet 模型中的重複信息,若是咱們可以去掉重複代碼就至關不錯。
相似與django提供Form類和ModelForm類,Rest Framework也包含了Serializer 類和 ModelSerializer類。
咱們看看使用ModelSerializer類重構後的序列化器,編輯snippets/serializers.py以下:
class SnippetSerializer(serializers.ModelSerializer):
class Meta: model =Snippet fields =('id','title','code','linenos','language','style')
Let's see how we can write some API views using our new Serializer class. For the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views.
讓咱們看看經過 Serializer 類怎樣來編寫 API 的視圖函數,如今咱們不會使用 REST 框架中的特性,僅僅寫原生的Django視圖函數。
We'll start off by creating a subclass of HttpResponse that we can use to render any data we return into json
.
咱們建立一個 HttpResponse 的子類,用來將任何數據轉換成 JSON格式
Edit the snippets/views.py
file, and add the following.
編輯snippets/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 its content into JSON. """ def __init__(self, data,**kwargs): content =JSONRenderer().render(data) kwargs['content_type']='application/json' super(JSONResponse, self).__init__(content,**kwargs)
The root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet.
咱們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() serializer =SnippetSerializer(snippets,many=True)
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) return JSONResponse(serializer.errors, status=400)
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.
注意,由於咱們要經過client向該view post一個請求,因此咱們要將該view 標註爲csrf_exempt, 以說明不是一個CSRF事件。它不一樣於以往的正常的視圖函數,REST框架的視圖函數事實上使用更加敏感的行爲,它如今只是爲了達到咱們的目的。
We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet.
咱們還要一個視圖函數來爲單獨的 snipet 實例服務,用來恢復,更新和刪除 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) return JSONResponse(serializer.errors, status=400) elif request.method =='DELETE': snippet.delete() return HttpResponse(status=204)
Finally we need to wire these views up. Create the snippets/urls.py
file:
將views.py保存,在Snippets目錄下面建立urls.py,添加如下內容:
from django.conf.urls import patterns, url urlpatterns = patterns('snippets.views', url(r'^snippets/$','snippet_list'), url(r'^snippets/(?P<pk>[0-9]+)/$','snippet_detail'),
)
It's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed json
, or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 "server error" response. Still, this'll do for now.
注意咱們有些邊緣事件沒有處理,服務器可能會拋出500異常。
Now we can start up a sample server that serves our snippets.
如今咱們啓動server來測試咱們的Snippet。
在python mange.py shell終端下執行(若是前面進入尚未退出)
>>quit() >> 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.python manage.py runserver
In another terminal window, we can test the server.
在另外一個終端窗口,咱們能夠測試服務
We can get a list of all of the snippets.
curl http://127.0.0.1:8000/snippets/
[
{"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"}
]
Or we can get a particular snippet by referencing its id.
或者咱們經過snippet id來獲取一個snippet實例:
curl http://127.0.0.1:8000/snippets/2/ {"id":2,"title":"","code":"print \"hello, world\"\n","linenos": false,"language":"python","style":"friendly"}
Similarly, you can have the same json displayed by visiting these URLs in a web browser.
相似地,你也能夠經過瀏覽器訪問 URL 來獲取 json。
We're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views.
到目前位置,咱們得到了一個用來序列化模型的 API 很是相似Django的Forms, 使用原生的Django視圖函數來寫API的視圖函數。
Our API views don't do anything particularly special at the moment, beyond serving json
responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API.
咱們的 API 視圖函數沒有作其餘事情,只是返回JSON數據,並且還有一些邊緣事件沒有清理,可是它已是一個可用的 API 了。