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

第一章 序列化

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

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

0. 介紹

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

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


1. 開始一個(gè)新的環(huán)境(虛擬環(huán)境)

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

virtualenv env
source env/bin/activate

現(xiàn)在我們在我們的虛擬環(huán)境中安裝我們需要的包

pip install django
pip install djangorestframework
pip install pygments          # 這個(gè)包我們將用來實(shí)現(xiàn)代碼高亮

2.碼代碼前最后的準(zhǔn)備

好的,我們從現(xiàn)在開始就要碼代碼了,期待麼,我們首先先創(chuàng)建一個(gè)項(xiàng)目吧

cd ~
django-admin.py startproject tutorial
cd tutorial

我們創(chuàng)建完項(xiàng)目之后再在項(xiàng)目目錄下創(chuàng)建一個(gè)app

python manage.py startapp snippets

我們現(xiàn)在在settings文件中把我們剛才創(chuàng)建的snippetsrest_framework加入到INSTALLED_APPS中,我們現(xiàn)在打開tutorial/settings.py文件:

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

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


3.開始創(chuàng)建我們的模型

我們打開我們的snippets/models.py文件,創(chuàng)建一個(gè)簡單的模型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',)

下一步我們創(chuàng)建數(shù)據(jù)庫遷移并執(zhí)行,然后同步數(shù)據(jù)庫。

python manage.py makemigrations snippets
python manage.py migrate

4.創(chuàng)建一個(gè) 序列化 類

在這一步,我們需要在我們的WEB Api中提供序列化和反序列化的代碼實(shí)例,如JSON格式。我們可以通過聲明序列化程序來完成這些,序列化的工作機(jī)制類似于Django的表單forms。我們現(xiàn)在在snippets目錄里命名一個(gè)叫serializers.py的文件,并在這個(gè)文件里添加如下代碼。

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):
        """
        創(chuàng)建并返回一個(gè)Snippets實(shí)例化對象,并且給出驗(yàn)證數(shù)據(jù)
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        更新并且返回一個(gè)已存在的Snippets實(shí)例化對象,并給出驗(yàn)證數(shù)據(jù)
        """
        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

這個(gè)類定義了我們需要序列化或者反序列化的字段, create()update()方法定義了在調(diào)用serializer.save()時(shí)如何創(chuàng)建或者修改完整的實(shí)例。

這個(gè)序列化的類和DjangoForms類很類似,并且在各個(gè)字段上有一些類似的驗(yàn)證標(biāo)志,如required, max_length,default

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

但事實(shí)上我們平時(shí)寫項(xiàng)目的時(shí)候不會這樣把一個(gè)一個(gè)的字段都列出來,我們通常使用ModelSerialzer

5. 操作序列化器

我們先進(jìn)入Django shell來熟悉下我們之前的代碼。
在項(xiàng)目目錄里輸入

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

我們的到了一個(gè)實(shí)例對象,我們來試一試將它序列化

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

這時(shí)我們把實(shí)例化對象轉(zhuǎn)換成了python的格式,如果我們需要完成序列化的過程,我們需要將它轉(zhuǎn)化為json格式。

content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'

反序列化也是類似的,首先我們將一個(gè)流解析為python的類型。

from django.utils.six import BytesIO         # six模塊是一個(gè)python2和python3的兼容性庫
stream = BytesIO(content)
data = JSONParser().parse(stream)

然后我們再將python的數(shù)據(jù)類型轉(zhuǎn)換成對象實(shí)例

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>

從這里我們可以注意到這個(gè)API工作起來和表單有多么的類似,當(dāng)我們開始用我們的序列化寫視圖函數(shù)的時(shí)候我們會感覺的更加明顯。

除此之外,我們還可以序列化查詢集而不是模型實(shí)例,我們在代碼中添加一個(gè)many=True的標(biāo)志到序列化程序的參數(shù)里就行了

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復(fù)制了大量在Snippet模型里有的信息,我們可以用另一種方法來使得我們的代碼更加簡潔。

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

我們現(xiàn)在使用ModelSerializer類來重構(gòu)我們的序列化器,我們再次打開serializer.py,用以下的內(nèi)容來代替之前寫的SnippetSerializer

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

序列化器有一個(gè)很好的屬性,就是我們可以打印它所有的字段在一個(gè)序列化的實(shí)例中,我們再次打開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``并沒有做什么神器的事情,它只是創(chuàng)建了序列化類的快捷方式。

  • 自動確定了所有字段
  • create()和update()方法都是簡單的默認(rèn)實(shí)現(xiàn)

7.使用序列化器來編寫一個(gè)常規(guī)的Django視圖

我們看看我們?nèi)绾问褂眯碌?code>serializer類來編寫API視圖,目前我們不使用REST Framework的其他功能,我們用Django常規(guī)的方式來寫。

首先我們渲染一個(gè)HttpResponse的子類,我們可以使用他來渲染任何我們返回的json數(shù)據(jù)。

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

同時(shí),我們在我們的這個(gè)API上創(chuàng)建列出所有已存在的實(shí)例,或者創(chuàng)建新的實(shí)例的視圖

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

注意,我們現(xiàn)在希望能從不具有CSRF令牌的客戶端來向此視圖POST數(shù)據(jù),我們需要將視圖用裝飾器@csrf_exempt裝飾,但這不是我們通常要做的,REST Framework中的視圖實(shí)際上會使用比這更加明智的行為,我們后面再說。

我們現(xiàn)在還需要一個(gè)與單個(gè)實(shí)例對應(yīng)的視圖,并可以用來檢索,更新和刪除操作。

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

好的,最后,我們需要連接這些視圖,我們來創(chuàng)建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')),
]

雖然,目前這樣做可能會出現(xiàn)一些狀況,服務(wù)器會報(bào)500錯(cuò)誤,但我們現(xiàn)在先這樣做,其他的問題后面再來解決。

8. 測試我們做的第一個(gè)API

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

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.

然后我們使用瀏覽器跳轉(zhuǎn)到http://127.0.0.1:8000/snippets/
我們將看到我們得到了一個(gè)所有的實(shí)例的列表

[
  {
    "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"
  }
]

然后我們再跳轉(zhuǎn)到http://127.0.0.1:8000/snippets/2/,我們可以看到我們id=2的實(shí)例

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

9. 我們現(xiàn)在走到哪了

我們現(xiàn)在擁有了第一個(gè)序列化API,然后寫了一個(gè)Django常規(guī)的視圖,在有一些錯(cuò)誤處理的情況下我們還需要改進(jìn),但是,但是,這確確實(shí)實(shí)是一個(gè)有功能的API了,為自己鼓掌吧!

下一階段我們將來講一講如何改進(jìn),請關(guān)注我們第二章的內(nèi)容。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容