- 1.Quickstart
- 1.1 項目搭建
- 1.2 序列化
- 1.3 視圖
- 1.4 URLs
- 1.5 設(shè)置
- 2.Serialization
- 2.1 創(chuàng)建一個模型
- 2.2 創(chuàng)建一個序列化類
- 2.3 使用Serializers
- 2.4 使用ModelSerializers
- 2.5 使用Serializer編寫常規(guī)的Django視圖
- 3.測試我們在Web API上的第一次訪問
1、Quickstart
環(huán)境
Python 3.7.2
MacOS High Sierra
django-2.2.1
djangorestframework-3.9.4
1.1 項目
我們將創(chuàng)建一個簡單的API來允許管理員用戶查看和編輯和系統(tǒng)中的用戶和組。
項目創(chuàng)建
創(chuàng)建目錄
mkdir tutorial
cd tutorial
創(chuàng)建virtualenv,環(huán)境隔離我們本地的依賴包關(guān)系
virtualenv env # 會得到下面結(jié)果回應(yīng)
Using base prefix '/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7'
New python executable in /Users/administrator/Desktop/Django-REST-Framework/tutorial/env/bin/python3.7
Also creating executable in /Users/administrator/Desktop/Django-REST-Framework/tutorial/env/bin/python
Installing setuptools, pip, wheel...
done.
source env/bin/activate #激活虛擬環(huán)境,可以看到以下結(jié)果顯示
(env) AdministratordeiMac:tutorial administrator$
在虛擬環(huán)境安裝Django 和 Django REST Framework
pip install --upgrade pip #先升級pip
pip install django # 安裝django
pip install djangorestframework # 安裝djangorestframework
pip install pygments #代碼高亮
創(chuàng)建項目,并建立一個app
django-admin startproject tutorial . #建立Django項目。后面"."表示在當(dāng)前文件夾建立項目
python manage.py startapp quickstart 或則django-admin.py startapp quickstart #新建一個APP
項目布局應(yīng)該如下:


第一次同步數(shù)據(jù)庫
python manage.py migrate #第一次同步數(shù)據(jù)庫,如下:

創(chuàng)建超級用戶,用戶名admin,密碼123456(超級用戶隨便起,但是要記住名字和密碼,后面要用到)
python manage.py createsuperuser

1.2 序列化
首先定義一些序列化器,創(chuàng)建一個名為tutorial/quickstart/serializers.py的文件,我們將用它來表示數(shù)據(jù)。
#文件路徑tutorial/quickstart/serializers.py
from django.contrib.auth.models import User, Group
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'group')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
field = ('url', 'name')
注意這里,我們使用了超鏈接關(guān)系(HyperlinkedModelSerializer)。你還可以使用主鍵和其他關(guān)系,但是這種超媒體引
擎驅(qū)動是不是很棒的RESTful設(shè)計呢。
1.3 視圖
在tutorial/quickstart/views.py中,輸入:
#文件路徑tutorial/quickstart/views.py
from rest_framework import viewsets
from .serializers import UserSerializer, GroupSerializer
from django.contrib.auth.models import User, Group
class UserViewSet(viewsets.ModelViewSet):
# 允許用戶查看或者編輯API端點
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
# 允許組查看或者編輯API端點
queryset = Group.objects.all()
serializer_class = GroupSerializer
我們將所有通用行為分組到
ViewSets類中,而不是編寫多個視圖。
如果需要,我們可以輕松第將這些視圖分解為單個視圖(這個后面會細(xì)說的),但是使用視圖集可以使視圖邏輯組織的非常好,
并且非常簡潔。
1.4 URLs
配置路由
# tutorial/urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from rest_framework import routers
from quickstart import views
router = routers.DefaultRouter()
router.register('users', views.UserViewSet)
router.register('group', views.GroupViewSet)
# 使用自動URL路由連接API
# 另外,我們還包括可以瀏覽API的登錄URL
urlpatterns = [
path('admin/', admin.site.urls),
url('^', include(router.urls)),
url('^api-auth/', include('rest_framework', namespace='rest_framework')),
]
因為我們使用視圖集而不是視圖,所以我們可以通過簡單地向路由器類注冊視圖集,來自動為我們的API生成URL conf。
同樣,如果我們需要更多地控制API URL,我們可以簡單地使用常規(guī)的基于類的視圖,并明確地編寫URL conf。
最后,我們將包括默認(rèn)的和注銷視圖,以用于可以瀏覽的API,這是可選的。但是如果你的API需要身份驗證,并且想使用可瀏覽的API,則會很有用。
1.5 設(shè)置(Setting)
將rest_framework 添加到INSTALLED_APPS.
設(shè)置路徑:tutorial/setting.py中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
]
第一階段已經(jīng)完成,下面測一下我們的API:開啟服務(wù)
python manage.py runserver
我們可以使用從命令行curl等工具訪問我們api:
(env) AdministratordeiMac:tutorial administrator$ curl -H 'Accept: application/son; indent=4' -u admin:123456 http://127.0.0.1:8000/users/
[
{
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin",
"email": "1232@qq.com",
"groups": []
}
]
或者命令行工具 httpie ...
(env) AdministratordeiMac:tutorial administrator$ http -a admin:123456http://127.0.0.1:8000/users/
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin",
"email": "1232@qq.com",
"groups": []
}
]
或者直接通過瀏覽器訪問 URL http://127.0.0.1:8000/users/ ,如下如所示:

2、Serialization(序列化)
添加上文創(chuàng)建APP quickstart
在tutorial/setting.py中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'quickstart',
]
2.1 創(chuàng)建一個模型
在quickstart/models.py文件中添加模型
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
# 提取出pyment 支持的所有語言的語法分析程序
LEXERS = [item for item in get_all_lexers() if item[1]]
# 提取除了'pyments' 支持的所有語言列表
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 提取出'pyment' 支持的所有格式化風(fēng)格列表
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=120)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=120)
class Meta:
ordering = ('-created',)
然后執(zhí)行數(shù)據(jù)遷移和同步
python manage.py makemigrations
Migrations for 'quickstart':
quickstart/migrations/0001_initial.py
- Create model Snippet
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, quickstart, sessions
Running migrations:
Applying quickstart.0001_initial... OK
2.2 創(chuàng)建一個序列化類
我們開始使用Web API 的第一件事就是提供一種,將代碼片段的實例序列化或者反序列化為表示形式(json等)的方法。我們可以通過聲明與Django forms非常相似的序列化器(serializers)實現(xiàn)。在上面我們已經(jīng)創(chuàng)建了serializers.py的文件,現(xiàn)在修改如下:
# 路徑quickstart/serializers.py
from rest_framework import serializers
from .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=120)
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):
# 給定驗證過的數(shù)據(jù)創(chuàng)建并返回一個新的Snippet實例。
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
# 根據(jù)已驗證的數(shù)據(jù)更新并返回已經(jīng)存在的Snippet實例
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()方法定義了在調(diào)用serializer.save()時如何創(chuàng)建和修改完整的實例。序列化器列與Django form類非常類似,在各種字段中,包含類似驗證標(biāo)志,例如
require,max_length和default字段標(biāo)識還可以控制serializer在某些情況下如何顯示,比如渲染HTML時候,上面
{'base_template':'textarea.html'},標(biāo)志等同于在Django Form類中使用widget=widget.Textarea.這對于控制如何顯示可以瀏覽的API 特別有用,我們將在后面講解看到。我們實際上也可以通過使用
ModelSerializer類來節(jié)省一些時間,我們稍后會看到,但是現(xiàn)在我們將使用我們明確定義的serializer。
2.3 使用Serializers
再進一步了解之間,我們先熟悉使用我們的新類Serializer類。讓我們進入Django sehll
python manage.py shell
我們已經(jīng)導(dǎo)入了幾個模塊,然后開始創(chuàng)建一些片段代碼來處理
AdministratordeiMac:tutorial administrator$ python manage.py shell
Python 3.7.2 (default, Feb 12 2019, 08:16:38)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from quickstart.models import Snippet
>>> from quickstart.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()
我們已經(jīng)有幾個可以使用的片段實例,讓我們看看序列化中的一個實例:
>>> serializer = SnippetSerializer(snippet)
>>> serializer.data
{'id': 2, 'title': '', 'code': 'print "Hello , World"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
>>>
此時,我們將模型實例轉(zhuǎn)換成為python原生數(shù)據(jù)類型。要完成序列化過程,我們將數(shù)據(jù)渲染成json
>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print \\"Hello , World\\"\\n","linenos":false,"language":"python","style":"friendly"}'
>>>
反序列化是類似的。首先我們將一個流解析為python原生的數(shù)據(jù)類型:
>>> from django.utils.six import BytesIO
>>>
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> data
{'id': 2, 'title': '', 'code': 'print "Hello , World"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
>>>
然后我們將這些原生的數(shù)據(jù)類型恢復(fù)正常的對象實例。
>>> serializer = SnippetSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print "Hello , World"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
>>> serializer.save()
<Snippet: Snippet object (3)>
注意API工作形式和表單(form)是多么相似。但我們開始使用我門的序列化類編寫視圖的時候,相似性將會更加明顯。
我們也可以序列化查詢集代替模型實例。為此,我們只需要在序列化參數(shù)中添加一個mangy = True
>>> serializer = SnippetSerializer(Snippet.objects.all(),many = True)
>>> serializer.data
[OrderedDict([('id', 3), ('title', ''), ('code', 'print "Hello , World"'), ('linenos', False),
('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''),
('code', 'print "Hello , World"\n'), ('linenos', False), ('language', 'python'), ('style',
'friendly')]), OrderedDict([('id', 1), ('title', ''), ('code', 'foo="bar"\n'), ('linenos',
False), ('language', 'python'), ('style', 'friendly')])]
>>>
2.4 使用ModelSerializers
我們在SnippetSerializer類中重復(fù)包含Snippet模型中的信息。如果能保持代碼簡潔,就像大家經(jīng)常說的don't repeat yourself。就像Django 提供了Form類和MoldelForm類一樣,RESTFramework包括Serializer類和ModelSerializers類一樣。我們試著看看使用ModelSerializer類重構(gòu)我們的序列化類。再次打開quickstrat/是、serializers類。并將SnippetSerializer類的內(nèi)容替換為下面內(nèi)容:
以前的SnippetSerializer類的內(nèi)容:
from rest_framework import serializers
from .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=120)
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):
# 給定驗證過的數(shù)據(jù)創(chuàng)建并返回一個新的Snippet實例。
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
# 根據(jù)已驗證的數(shù)據(jù)更新并返回已經(jīng)存在的Snippet實例
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
替換如下:
from rest_framework import serializers
from .models import Snippet
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
序列化程序有一個非常棒的屬性,就是可以通過打印其結(jié)構(gòu)(representation)來檢查序列化實例中的所有字段。
>>> from quickstart.serializers import SnippetSerializer
>>> serialzier = SnippetSerializer()
>>> print(repr(serializer))
SnippetSerializer(<QuerySet [<Snippet: Snippet object (3)>, <Snippet: Snippet object (2)>, <Snippet: Snippet object (1)>]>, many=True):
id = IntegerField(read_only=True)
title = CharField(allow_blank=True, max_length=120, required=False)
code = CharField(style={'base_template': 'textarea.html'})
linenos = BooleanField(required=False)
language = ChoiceField(choices=[('abap', 'ABAP'), ('abnf', 'ABNF'), ('ada', 'Ada'), ('adl', 'ADL'), ('agda', 'Agda'), .... ('zephir', 'Zephir')], default='python')
style = ChoiceField(choices=[('abap', 'abap'),.... ('trac', 'trac'), ('vim', 'vim'), ('vs', 'vs'), ('xcode', 'xcode')], default='friendly')
>>>
注意ModelSerializer類并不會做任何特別神奇的事情,他們只是創(chuàng)建序列化器類的快捷方式:
- 自動確定一組字段(不用重復(fù)去定義類屬性)。
- 默認(rèn)簡單的實現(xiàn)
create()和update()方法。
2.5 使用我們的Serializer編寫常規(guī)的Django視圖
我們嘗試使用我們的Serializer類編寫一些API視圖。目前我們不用任何REST Framework 的其他功能,我么只編寫常規(guī)的Django視圖。
路徑 : quickstart/view.py,下面是我們開始寫的內(nèi)容
from rest_framework import viewsets
from .serializers import UserSerializer, GroupSerializer
from django.contrib.auth.models import User, Group
class UserViewSet(viewsets.ModelViewSet):
# 允許用戶查看或者編輯API端點
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
# 允許組查看或者編輯API端點
queryset = Group.objects.all()
serializer_class = GroupSerializer
然后替換如下:
quickstart/view.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 quickstart.models import Snippet
from quickstart.serializers import SnippetSerializer
@csrf_exempt
def snippet_list(request):
# 列出所有代碼 snippet, 或者創(chuàng)建一個新的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到此視圖,所以我們需要將該視圖標(biāo)記為@csrf_exempt.這不是你通常想要做的事情,并且RESTframework視圖實際上比這更具實用行為,但是他現(xiàn)在足夠達(dá)到我們的目的。
我們還需要一個與單個相對應(yīng)的視圖,并可用于檢索、更新或者刪除snippet.
quickstart/view.py #
@csrf_exempt
def snippet_detail(request, pk):
# 獲取、更新或者刪除一個代碼 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)
最后,我們需要把這些駛?cè)腈溄悠饋?,?chuàng)建quickstart/urls.py 文件
from django.conf.urls import url
from .views import snippet_list, snippet_detail
urlpatterns = [
url('^quickstart/$', snippet_list),
url('^quickstart/(?P<pk>[0-9]+)/$', snippet_detail),
]
另外需要在tutorial/urls.py中吧root urlconf來包含我們的quickstart應(yīng)用的urls。修改為下面內(nèi)容:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
urlpatterns = [
path('admin/', admin.site.urls),
url('^', include('quickstart.urls')),
]
3. 測試我們在Web API上的第一次訪問
啟動quickstart服務(wù),我們先退出shell
quit()
啟動Django開發(fā)服務(wù)
AdministratordeiMac:tutorial administrator$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
May 27, 2019 - 07:13:55
Django version 2.2.1, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
在瀏覽器中輸入http://127.0.0.1:8000/quickstart/

或者安裝httpie
pip install httpie
然后在終端輸入:http http://127.0.0.1:8000/quickstart/,如下
