django rest framework快速入門第一章 序列化

第一章 序列化

寫在前面:
本文翻譯于django rest framework官方文檔,由于網上大多數django rest framework中文翻譯文檔都有較為多的刪減行為,筆者在學習的時候就覺得不是太方便,故筆者將官方文檔較為完善的為大家翻譯下,僅供大家學習參考。

由于筆者文筆有限,若有寫得不當之處,敬請各位同仁指出;若涉及到侵權,請聯系筆者,筆者將立即刪除。

0. 介紹

本教程將為大家創建一個簡單的代碼高亮的Web API,在這個過程中,我們將為你介紹組成REST framework的各種組件,并且讓你了解這些組件是如何配合工作的。

整篇教程是非常深入的,所以在開始的時候你最好準備好炸雞和啤酒,以防你中途睡著,如果你只想要對這個框架有一個快速的大致的了解,你就可以來看這篇快速入門文檔。


1. 開始一個新的環境(虛擬環境)

在開始之前我們還有很重要的一步,那就是創建虛擬環境,如果你對虛擬環境不太了解的話,筆者向你推薦這篇文章python virtualenv使用教程

virtualenv env
source env/bin/activate

現在我們在我們的虛擬環境中安裝我們需要的包

pip install django
pip install djangorestframework
pip install pygments          # 這個包我們將用來實現代碼高亮

2.碼代碼前最后的準備

好的,我們從現在開始就要碼代碼了,期待麼,我們首先先創建一個項目吧

cd ~
django-admin.py startproject tutorial
cd tutorial

我們創建完項目之后再在項目目錄下創建一個app

python manage.py startapp snippets

我們現在在settings文件中把我們剛才創建的snippetsrest_framework加入到INSTALLED_APPS中,我們現在打開tutorial/settings.py文件:

INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

OK,我們的準備工作就做好啦(We are ready to roll)


3.開始創建我們的模型

我們打開我們的snippets/models.py文件,創建一個簡單的模型Snippet

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

4.創建一個 序列化 類

在這一步,我們需要在我們的WEB Api中提供序列化和反序列化的代碼實例,如JSON格式。我們可以通過聲明序列化程序來完成這些,序列化的工作機制類似于Django的表單forms。我們現在在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):
        """
        創建并返回一個Snippets實例化對象,并且給出驗證數據
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        更新并且返回一個已存在的Snippets實例化對象,并給出驗證數據
        """
        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()時如何創建或者修改完整的實例。

這個序列化的類和DjangoForms類很類似,并且在各個字段上有一些類似的驗證標志,如required, max_length,default

字段標志還可以控制在某些情況下如何顯示序列化程序,例如在呈現為HTML時。 上面的{'base_template':'textarea.html'}標志等同于在Django Form類上使用widget = widgets.Textarea 這對于控制如何顯示可瀏覽的API特別有用,這里我們將在后面看到。

但事實上我們平時寫項目的時候不會這樣把一個一個的字段都列出來,我們通常使用ModelSerialzer

5. 操作序列化器

我們先進入Django shell來熟悉下我們之前的代碼。
在項目目錄里輸入

python manage.py 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         # six模塊是一個python2和python3的兼容性庫
stream = BytesIO(content)
data = JSONParser().parse(stream)

然后我們再將python的數據類型轉換成對象實例

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 = 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')])]

6.使用ModelSerailizer

我們之前寫的SnippetSerializer復制了大量在Snippet模型里有的信息,我們可以用另一種方法來使得我們的代碼更加簡潔。

Django提供的Form類和ModelForm類相似,REST Framework也為我們提供了Serializer類和ModelSerializer類。

我們現在使用ModelSerializer類來重構我們的序列化器,我們再次打開serializer.py,用以下的內容來代替之前寫的SnippetSerializer

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

序列化器有一個很好的屬性,就是我們可以打印它所有的字段在一個序列化的實例中,我們再次打開Django 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()方法都是簡單的默認實現

7.使用序列化器來編寫一個常規的Django視圖

我們看看我們如何使用新的serializer類來編寫API視圖,目前我們不使用REST Framework的其他功能,我們用Django常規的方式來寫。

首先我們渲染一個HttpResponse的子類,我們可以使用他來渲染任何我們返回的json數據。

打開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)

同時,我們在我們的這個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)

    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 Framework中的視圖實際上會使用比這更加明智的行為,我們后面再說。

我們現在還需要一個與單個實例對應的視圖,并可以用來檢索,更新和刪除操作。

@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 == '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,以及我們snippets app的URL

from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippets.urls')),
]

雖然,目前這樣做可能會出現一些狀況,服務器會報500錯誤,但我們現在先這樣做,其他的問題后面再來解決。

8. 測試我們做的第一個API

我們先退出shell,然后開啟本地服務器。

python manage.py runserver

Validating models...

0 errors found
Django version 1.8.3, using settings 'tutorial.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

然后我們使用瀏覽器跳轉到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"
  }
]

然后我們再跳轉到http://127.0.0.1:8000/snippets/2/,我們可以看到我們id=2的實例

{
  "id": 2,
  "title": "",
  "code": "print \"hello, world\"\n",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

9. 我們現在走到哪了

我們現在擁有了第一個序列化API,然后寫了一個Django常規的視圖,在有一些錯誤處理的情況下我們還需要改進,但是,但是,這確確實實是一個有功能的API了,為自己鼓掌吧!

下一階段我們將來講一講如何改進,請關注我們第二章的內容。

謝謝各位閱讀,并歡迎各位留下寶貴意見

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容