django rest framework個人學習筆記(三)————Tutorial1.Serialization

張懟懟發表於2019-02-16

Serialization——序列化

官網地址

前言

本教程將會介紹建立一個簡單webAPI。在整個學習的過程中,將會逐一介紹到REST framework的各種元件,讓你全面的瞭解所有東西是如果組合在一起的。
這個教程將會相當的詳細,所以在開始之前,你應該去來點餅乾,再喝上一些你喜歡的飲料。如果你只是想快速概覽,你應該檢視Quickstart.
注意:本教程的程式碼可以在GitHub的 tomchristie/rest-framework-tutorial中找到。測試,這是一個測試連結。

建立一個新的虛擬環境

使用 virtualenv建立一個虛擬環境.

virtualenv env
source env/bin/activate

然後我們在我們建立的虛擬環境中安裝需要的包。

pip install django
pip install djangorestframework
pip install pygments #使用它來程式碼高亮

注意:使用 deactivate來隨時退出虛擬環境。更多的資訊請檢視Virtualenv documentation

準備

我們這邊開始編寫程式碼了。先來建立一個專案吧~

cd ~
django-admin.py startproject tutorial
cd tutorial

接下來,我們建立一個app。

python manage.py startapp snippets

我們需要新增 snippetsrest_frameworkINSTALLED_APPS,在 tutorial/settings.py檔案中:

INSTALLED_APPS = {
    ...
    `rest_framework`,
    `snippets.apps.SnippetsConfig`,
}

注意:如果你使用的django<1.9,你需要替換 snippets.apps.SnippetsConfigsnippets
我們可以繼續上路了。

建立model

在本教程中,我們將建立一個簡單的model,這個model將用於儲存程式碼片段。
編輯 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,defalut=``)
    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

建立Serializer

首先,我們需要給我們的web API 提供一種序列化和反序列化的呈現方式,如json
我們可以申明一個和django的forms差不多的serializers。建立一個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):
        return Snippet.objects.create(**validated_data)
       
    def update(self,instance,validated_data):
        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

serializer的第一步是定義需要 序列化/反序列化 的欄位。create()update()方法定義當serializer.save()時,如果建立和更新。
serializer類非常類似於Django的Form類。包括各個欄位的驗證標記,例如 required,max_length,default
欄位標記也能夠控制serializer在某些環境中如何顯示,例如呈現為HTML.
之前的{`base_template`: `textarea.html`}標記相當於django Form中的widget=widgets.Textarea`。

其實我們使用ModelSerializer類更能夠節省我們的時間,但現在,我們還是顯示的定義我們的欄位。

開始工作

在我們更進一步學習之前,我們將通過django shell來熟悉serializer。

python manage.py shell

我們需要匯入我們需要的東西,然後建立兩個snippet。

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"
`)
snippet.save()

snippet = Snippet(code=`print "hello, world"
`)
snippet.save()

我們來看看我們建立的其中一個例項:

serializer = SnippetSerializer(snippet)
serializer.data
# {`id`: 2, `title`: u``, `code`: u`print "hello, world"
`, `linenos`: False, `language`: u`python`, `style`: u`friendly`}

在這裡,我們翻譯這個model例項為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
data = JSONParser().parse(stream)

然後我們還原原生資料型別到一個完全填充的物件例項中。

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>

注意:這類似於forms.當我們編寫views使用serializer時,這會看起來更相似。

serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([(`id`, 1), (`title`, u``), (`code`, u`foo = "bar"
`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)]), OrderedDict([(`id`, 2), (`title`, u``), (`code`, u`print "hello, world"
`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)]), OrderedDict([(`id`, 3), (`title`, u``), (`code`, u`print "hello, world"`), (`linenos`, False), (`language`, `python`), (`style`, `friendly`)])]

使用ModelSerializers

我們的SnippetSerializer類複製了很多snippet中的東西。我們希望我們的程式碼能夠保持乾淨整潔。
類似於django有Form和ModelForm類,REST framework也包含SerializerModelSerializer兩個類。
讓我們來使用ModelSerializer來重構我們的serializer類。開啟snippets/serializers.py,替換SinppetSerializer類:

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

serializer擁有一個很好的屬性,你能夠通過列印檢視一個serializer例項的所有欄位。開啟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不會做任何如魔法般的事情,它只是更快捷的建立serializer

  • 自動的確定欄位的設定
  • 簡單的預設create()update()方法

使用Serializer寫一個常規的django views

讓我們使用我們新的serializer來寫一些API檢視吧。在這裡,我們不會使用任何REST framework的其他特性,我們就寫一個常規的 django views。
來編寫 snippets/views.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 snippets.models import Snippet
from snippets.serializers import SnippetSerializer

我們的API將會支援檢視所有資料和建立一個新的snippet

@csrf_exempt
def snippet_list(request):
    """
    顯示所有的程式碼片段,或者建立一個新的片段
    """
    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)

注意:因為我們希望post能夠不通過CSRF token的驗證,所以我們使用了 csrf_exempt。事實上,這不會是你經常做的事情,並且REST framework views會更加明智的做這個事情。但是現在,他可以使用。

我們還需要這個檢視能夠檢索,更新和刪除。

@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)

最後,我們需要建立url,在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),
]

我們還需要在跟urls.py(tutorial/urls.py)上引入我們的urls.py

from django.conf.urls import url, include

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

注意:我們的檢視有某些問題是還沒有做妥善的處理,比如傳送的json格式不正確,或是呼叫檢視沒有的方法,那麼我們就會返回一個500“伺服器錯誤”的訊息。

相關文章