在咱們作任何事情以前,咱們將使用virtualenv建立一個新的虛擬環境。這將確保咱們的包配置與咱們正在開展的任何其餘項目保持良好的隔離。html
virtualenv env source env/bin/activate
如今咱們在一個virtualenv環境中,咱們能夠安裝咱們的包的要求。前端
pip install django pip install djangorestframework pip install pygments # We'll be using this for the code highlighting
注意:要隨時退出virtualenv環境,只需鍵入deactivate
。有關更多信息,請參閱virtualenv文檔。python
好的,咱們準備好得到編碼。要開始,咱們來建立一個新的項目來處理git
cd ~ django-admin.py startproject tutorial cd tutorial
一旦完成,咱們能夠建立一個咱們將用來建立一個簡單的Web API的應用程序。
python manage.py startapp snippets
咱們須要添加咱們的新snippets
應用和rest_framework
應用INSTALLED_APPS
。咱們來編輯tutorial/settings.py
文件:
snippetsrest_frameworkINSTALLED_APPStutorial/settings.py
INSTALLED_APPS = ( ... 'rest_framework', 'snippets.apps.SnippetsConfig', )
請注意,若是你使用的Django <1.9,則須要更換snippets.apps.SnippetsConfig
有snippets
。github
好的,咱們準備好了。web
爲了本教程的目的,咱們將首先建立一個Snippet
用於存儲代碼片斷的簡單模型。繼續編輯snippets/models.py
文件。注意:良好的編程實踐包括評論。雖然您將在本教程代碼的存儲庫版本中找到它們,但咱們在此忽略了它們,專一於代碼自己。正則表達式
#coding=utf-8
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())
'''在models裏面,用的models. '''
class Snippet(models.Model):
create = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100,blank=True,default='')
code = models.TextField() #文本
lineons = 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',) #關鍵字的高亮顯示,排序方式是created 注意這裏是一個元祖
咱們還須要爲咱們的代碼段模型建立初始遷移,並首次同步數據庫。
python manage.py makemigrations snippets python manage.py migrate
咱們須要開始使用Web API的第一件事是提供一種將代碼片斷實例序列化和反序列化爲表示形式的方法json
。咱們能夠經過聲明與Django表單很是類似的序列化器來作到這一點。在snippets
命名的目錄中建立一個文件,serializers.py
並添加如下內容。shell
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES #從以前的models裏面導入進來
'''views裏面的全部字段,必需要與前面的models一致,儘量的原則是隻能少,不能多'''
'''在serializer裏面是 serializers. '''
class SnippetSerializer(serializers.Serializer): #Serializer是不可變的 Snippet是可變的,可是必定要和前面的models裏面的要一致
id = serializers.IntegerField(read_only=True) #id是可讀的,不能修改
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,validate_data):
return Snippet.objects.create(**validate_data)
#用於更新的函數
def update(self, instance, validated_data): #最後面instance.title 這是默認值
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
。django
字段標誌還能夠控制在某些狀況下,如渲染到HTML時如何顯示串行器。{'base_template': 'textarea.html'}
上面的標誌至關於widget=widgets.Textarea
在Django Form
類上使用。這對於控制如何顯示可瀏覽的API特別有用,咱們將在本教程的後面看到。
咱們實際上也能夠經過使用ModelSerializer
課程來節省本身的時間,咱們稍後會看到,可是如今咱們將保持咱們的序列化器定義。
在咱們進一步瞭解以前,咱們將熟悉使用咱們新的Serializer類。咱們進入Django shell。
python manage.py shell
好的,一旦咱們有幾個導入,咱們來建立一些代碼片斷來處理。
from snippets.models import Snippet from snippets.serializers import SnippetSerializer from rest_framework.renderers import JSONRenderer #輸出格式爲json格式 from rest_framework.parsers import JSONParser #輸入格式爲json格式 snippet = Snippet(code='foo = "bar"\n') #實例化類 snippet.save() snippet = Snippet(code='print "hello, world"\n') snippet.save()
咱們如今有幾個片斷實例能夠玩。咱們來看看序列化這些實例之一。
此時,咱們將模型實例轉換爲Python本機數據類型。爲了完成序列化過程,咱們將數據轉換成。
serializer = SnippetSerializer(snippet) serializer.data #獲取數據 python數據類型 # {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}json
content = JSONRenderer().render(serializer.data) #將python對象轉化爲json格式,由於前端傳來的數據只支持json格式 content # b'{"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')])]
咱們的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()
方法。咱們來看看咱們如何使用咱們的新的Serializer類編寫一些API視圖。目前咱們不會使用任何REST框架的其餘功能,咱們只需將視圖編寫爲常規的Django視圖。
編輯snippets/views.py
文件,並添加如下內容。
from django.http import HttpResponse,JsonResponse #HTTP響應和json相應
from django.views.decorators.csrf import csrf_exempt #b不用跨域請求了
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet #從模型中引入
from snippets.serializers import SnippetSerializer #引入serializers
咱們的API的根本將是一個視圖,支持列出全部現有的片斷,或建立一個新的片斷。
@csrf_exempt
def snippet_list(request): #生成列表,多條信息
#列出所有代碼片斷,或則建立新代碼片斷
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets,many=True) #轉成json格式
return JsonResponse(serializer.data,safe=False) #以json格式響應出去
elif request.method == 'POST':
data = JSONParser().parse(request) #解析發來的數據,用parse方法
serializer = SnippetSerializer(data=data)
if serializer.is_valid(): #驗證數據是否合法
serializer.save()
return JsonResponse(serializer.data,status=201) #返回操做成功
return JsonResponse(serializer.errors,status=400) #返回操做錯誤
咱們的API的根本將是一個視圖,支持列出全部現有的片斷,或建立一個新的片斷。
請注意,由於咱們但願可以從不具備CSRF令牌的客戶端對此視圖進行POST,所以咱們須要將視圖標記爲csrf_exempt
。這不是你一般想要作的事情,REST框架視圖實際上比這更有明確的行爲,但它如今將用於咱們的目的。
咱們還須要一個與單個代碼段對應的視圖,並可用於檢索,更新或刪除代碼段。
#設置容許能夠跨域請求
@csrf_exempt
def snippet_detail(request, pk):
"""
提取 更新 刪除代碼片斷
"""
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 == 'PATCH':#更新或者修改數據
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), #用戶用正則表達式出入PK,傳入到views裏面 修改,更新接口 ]
咱們還須要在tutorial/urls.py
文件中鏈接根urlconf ,以包含咱們的片斷應用程序的URL。
from django.conf.urls import url, include urlpatterns = [ url(r'^', include('snippets.urls')),
url(r'^admin/', admin.site.urls),
]
值得注意的是,咱們目前尚未正確處理的幾個邊緣案例。若是咱們發送格式錯誤json
,或者若是請求是使用視圖不處理的方法,那麼咱們最終會出現500個「服務器錯誤」響應。不過,如今這樣作。
如今咱們能夠啓動一個服務咱們的代碼片斷的示例服務器。
退出外殼...
quit()
...並啓動Django的開發服務器。
python manage.py runserver Validating models... 0 errors found Django version 1.11, using settings 'tutorial.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
在另外一個終端窗口中,咱們能夠測試服務器。
咱們可使用curl或httpie來測試咱們的API 。Httpie是用Python編寫的用戶友好的http客戶端。咱們來安裝
您可使用pip安裝httpie:
pip install httpie
最後,咱們能夠獲得全部片斷的列表:
或者咱們能夠經過引用其id來獲取特定的代碼段:
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" } ]
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。
咱們到目前爲止,咱們作得還不錯,咱們有一個序列化API,與Django的Forms API和一些常規的Django視圖很是類似。
咱們的API視圖目前沒有特別的特別之處,除了提供json
響應外,還有一些錯誤處理咱們仍然想要清理的邊緣狀況,但它是一個運行良好的Web API。