Django_REST Framework -2.序列化

序列化

環(huán)境搭建

首先我們先新建一個(gè) restapi 項(xiàng)目并安裝上 django-rest-framework (DRF) 環(huán)境

$ pip install djangorestframework
$ python manage.py startnewproject restapi
$ cd restapi
$ python manage.py startnewapp douban

接著,我們需要在 setting.py 里的加入如下代碼:

INSTALLED_APPS = (
    ...
    'rest_framework',
    'douban',
)

建立模型

由于我炒雞喜歡看電影族操,所以仿著 douban-API 來(lái)做個(gè)簡(jiǎn)易的豆瓣電影的 rest-api 蛹含。

所以我們就用這個(gè)「仿豆瓣電影 api 」來(lái)作為栗子開始教程吧鲁豪!

編輯 douban/models.py 文件并加入以下代碼:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.db import models

# 舉個(gè)栗子
COUNTRY_CHOICES = (
    ('US', 'US'),
    ('Asia', 'Asia'),
    ('CN', 'CN'),
    ('TW', 'TW'),
)
TYPE_CHOICES = (
    ('Drama', 'Drama'),
    ('Thriller', 'Thriller'),
    ('Sci-Fi', 'Sci-Fi'),
    ('Romance', 'Romance' ),
    ('Comedy', 'Comedy')
)
GENDER_CHOICES = (
    ('male', 'male'),
    ('female', 'female')
)

class movies(models.Model):
    title = models.CharField(max_length=100, blank=True, default='')
    year = models.CharField(max_length=20)
    # 在 director 關(guān)聯(lián)了 movies 類 和 celecrity 類, 在第4章會(huì)用到 celebrity 類
    # director = models.ForeignKey('celebrity', related_name='movies')
    country = models.CharField(choices=COUNTRY_CHOICES, default='US', max_length=20)
    type = models.CharField(choices=TYPE_CHOICES, default='Romance', max_length=20)
    rating = models.DecimalField(max_digits=3, decimal_places=1)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('created',)

# class celebrity(models.Model):
#     name = models.CharField(max_length=100, blank=True, default='')
#     age = models.IntegerField()
#     gender = models.CharField(choices=GENDER_CHOICES, default='男', max_length=20)

接著在終端中運(yùn)行:

$ python manage.py makemigrations douban
$ python manage.py migrate
$ python manage.py syncdb

來(lái)創(chuàng)建一個(gè)新的 migrations 并在數(shù)據(jù)庫(kù)中生成表。

創(chuàng)建序列化類

在開始構(gòu)建 Web API 時(shí),我們首先要做的就是提供對(duì) movies 實(shí)例的序列化和反序列化(即對(duì)序列化后的實(shí)例進(jìn)行「解碼」)奏路,這樣才能生成可供瀏覽的 json 格式的 api 阔蛉。我們可以通過(guò)聲明「序列器」(一個(gè)和 Django 表單十分類似的玩意兒)來(lái)做到這一點(diǎn)弃舒。

restapi 目錄中創(chuàng)建一個(gè) serializer.py 文件,加入以下代碼:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from rest_framework import serializers
from douban.models import movies, COUNTRY_CHOICES, TYPE_CHOICES

class MoviesSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    year = serializers.CharField(max_length=20)
    country = serializers.ChoiceField(choices=COUNTRY_CHOICES, default='US')
    type = serializers.ChoiceField(choices=TYPE_CHOICES, default='Romance')
    rating = serializers.DecimalField(max_digits=3, decimal_places=1)

    def create(self, validated_data):
        """
        根據(jù)接收到的 validated_data 創(chuàng)建一個(gè) movies 實(shí)例
        """
        return movies.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        根據(jù)接收到的 validated_data 更新并返回一個(gè) movies 實(shí)例
        """
        instance.title = validated_data.get('title', instance.title)
        instance.year = validated_data.get('year', instance.year)
        instance.country = validated_data.get('country', instance.country)
        instance.type = validated_data.get('type', instance.type)
        instance.rating = validated_data.get('rating', instance.rating)
        instance.save()
        return instance

序列器的第一個(gè)部分定義了要進(jìn)行序列化/反序列化的字段。

create()update() 方法定義了符合規(guī)范的 movies 實(shí)例的創(chuàng)建和更新的方法聋呢。

序列器非常類似于 Django Form 表單苗踪,它包含了幾種對(duì)字段常見(jiàn)的驗(yàn)證標(biāo)識(shí)符,如 required 削锰、 max_length 通铲、 default 等。這些標(biāo)識(shí)符實(shí)現(xiàn)的功能類似于 Django 表單器贩,就不詳細(xì)解釋了颅夺。

所以序列器實(shí)現(xiàn)了以下兩個(gè)功能:

  • 選擇相應(yīng)的模型
  • 選擇要展現(xiàn)的字段(驗(yàn)證后的)

我們也可以通過(guò)使用 ModelSerializer 多快好省地的構(gòu)建序列器,這個(gè)我們?nèi)蘸笤僬f(shuō)蛹稍。

開始使用序列器

在開始項(xiàng)目之前吧黄,我們先熟悉下序列器,在終端中啟動(dòng) Django shell :

$ python manage.py shell

輸入以下代碼來(lái)創(chuàng)建2個(gè) Movies 實(shí)例

「荒野獵人」和「蝙蝠俠愛(ài)上超人」

from douban.models import Movies
from douban.serializer import MoviesSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

movies = Movies(title='The Revenant', year='2015', country='US', type='Drama', rating=7.9)
movies.save()

movies = Movies(title='Batman v Superman: Dawn of Justice',  year='2016', country='US', type='Romance', rating=6.7)
movies.save()

然后將其中一個(gè)實(shí)例序列化

serializer = MoviesSerializer(movies)
serializer.data

#{'rating': u'7.9', 'title': u'The Revenant', 'country': 'US', 'year': u'2015', 'pk': None, 'type': 'Drama'}

接著我們將以上數(shù)據(jù)轉(zhuǎn)換為 JSON 格式唆姐,實(shí)現(xiàn)序列化

content = JSONRenderer().render(serializer.data)
content

#{"pk":null,"title":"The Revenant","year":"2015","country":"US","type":"Drama","rating":"7.9"}'

反序列化也類似拗慨,通過(guò)解析 Python 數(shù)據(jù)流并將數(shù)據(jù)流"引入"實(shí)例中即可

from django.utils.six import BytesIO

stream = BytesIO(content)
data = JSONParser().parse(stream)
serializer = MoviesSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
#OrderedDict([(u'title', u'The Revenant'), (u'year', u'2015'), (u'country', 'US'), (u'type', 'Drama'), (u'rating', Decimal('7.9'))])

可見(jiàn), serializer和django form 有多么相似, 當(dāng)我們寫view時(shí), 這一相似性會(huì)更加明顯.

當(dāng)我們輸入?yún)?shù)many=True時(shí), serializer還能序列化queryset:

serializer = MoviesSerializer(Movies.objects.all(), many=True)
serializer.data
[OrderedDict([('pk', 1), ('title', u'Batman v Superman: Dawn of Justice'), ('year', u'2016'), ('country', 'US'), ('type', 'Romance'), ('rating', u'6.7')]), OrderedDict([('pk', 2), ('title', u'The Revenant'), ('year', u'2015'), ('country', 'US'), ('type', 'Drama'), ('rating', u'7.9')])]

使用更高級(jí)的 ModelSerializers

接著如果你按照官網(wǎng)的教程走下去,你會(huì)發(fā)現(xiàn)上面的 serializer.py 是個(gè)代碼冗雜的序列器厦酬,這不符合 Python 的風(fēng)格胆描。

所以我們要做的就是簡(jiǎn)化代碼。

DRF 提供了更為簡(jiǎn)便的 ModelSerializer 類可以解決這個(gè)問(wèn)題仗阅。

所以我們修改之前的 serializer.py :

class MoviesSerializer(serializers.ModelSerializer):
    class Meta:
        model = Movies
        fields = ('id', 'title', 'year', 'country', 'type', 'rating')

這種模式的序列器可以很方便地檢查 fields 中的每個(gè)字段

然后在終端中打開 Django shell

$ python manage.py shell

輸入以下代碼

from douban.serializer import MoviesSerializer
serializer = MoviesSerializer()
print(repr(serializer))

#MoviesSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(allow_blank=True, max_length=100, required=False)
    year = CharField(max_length=20)
    country = ChoiceField(choices=(('US', 'US'), ('Asia', 'Asia'), ('CN', 'CN'), ('TW', 'TW')), required=False)
    type = ChoiceField(choices=(('Drama', 'Drama'), ('Thriller', 'Thriller'), ('Sci-Fi', 'Sci-Fi'), ('Romance', 'Romance'), ('Comedy', 'Comedy')), required=False)
    rating = DecimalField(decimal_places=1, max_digits=3)

注: ModelSerializer 類僅僅是創(chuàng)建 serializer 類的一個(gè)快捷方法昌讲,它除了實(shí)現(xiàn)以下兩種方法外并沒(méi)有其余的功能:

  • 聲明需要展現(xiàn)的字段
  • 定義 create()update() 方法

使用 Django views 編寫序列器視圖

為了更好理解序列器,我們不使用 DRF 的其他特性减噪,僅僅用 Django views 模式來(lái)編寫序列器的視圖短绸。

我們會(huì)創(chuàng)建一個(gè) HttpResponse 的子類,這樣就能將數(shù)據(jù)以 json 格式返回筹裕。

編輯 douban/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 douban.models import Movies
from douban.serializer import MoviesSerializer

class JSONResponse(HttpResponse):
    """
    將數(shù)據(jù)轉(zhuǎn)為 JSON 格式的 HttpResponse 子類
    """
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

講道理的話醋闭,我們 api 的根目錄應(yīng)該能羅列出所有的 Movies 或者 能新建一個(gè) Movies

并且還需要一個(gè)用于展示、更新和刪除 Movies 的 views

編輯 douban/views.py 加入以下代碼:

#!/usr/bin/python
# -*- coding: utf-8 -*-

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 douban.models import Movies
from douban.serializer import MoviesSerializer

class JSONResponse(HttpResponse):
    """
    將數(shù)據(jù)轉(zhuǎn)為 JSON 格式的 HttpResponse 子類
    """
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

@csrf_exempt
def movies_list(request):
    """
    羅列出所有的 Movies 或者 能新建一個(gè) Movies
    """
    if request.method == 'GET':
        movies = Movies.objects.all()
        serializer = MoviesSerializer(movies, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = MoviesSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)

@csrf_exempt
def movies_detail(request, pk):
    """
    展示\更新或刪除一個(gè) Movies
    """
    try:
        movies = Movies.objects.get(pk=pk)
    except Movies.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = MoviesSerializer(movies)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = MoviesSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        movies.delete()
        return HttpResponse(status=204)

我不是很弄明白這里關(guān)掉 csrf 的意義朝卒,那不如直接就不用 csrf 不就好了证逻?

不管了,先放著抗斤,以后回來(lái)看 ( 吐舌頭

最后修改 douban/url.py 導(dǎo)入相應(yīng)的視圖

from django.conf.urls import url
from douban import views

urlpatterns = [
    url(r'^dbmovies/$', views.movies_list),
    url(r'^dbmovies/(?P<pk>[0-9]+)/$', views.movies_detail),
]

并在 restapi/url.py 中 include 一下

from django.conf.urls import url, include

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

這樣 url 和 views 就綁定好了囚企。

測(cè)試 Web API

在終端中輸入

$ python manage.py runserver

接著來(lái)瀏覽器中訪問(wèn) http://127.0.0.1/dbmovies/

apitest

如果出現(xiàn)如圖所示的 api 則說(shuō)明 Web api 返回成功。

(順便安利一個(gè) chrome 插件 — FeHelper 可以自動(dòng)格式化 JSON 代碼)

https://github.com/thehackercat/django-rest-framework-tutorial/blob

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瑞眼,一起剝皮案震驚了整個(gè)濱河市龙宏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伤疙,老刑警劉巖银酗,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡黍特,警方通過(guò)查閱死者的電腦和手機(jī)蛙讥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)衅澈,“玉大人键菱,你說(shuō)我怎么就攤上這事〗癫迹” “怎么了经备?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)部默。 經(jīng)常有香客問(wèn)我侵蒙,道長(zhǎng),這世上最難降的妖魔是什么傅蹂? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任纷闺,我火速辦了婚禮,結(jié)果婚禮上份蝴,老公的妹妹穿的比我還像新娘犁功。我一直安慰自己,他們只是感情好婚夫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布浸卦。 她就那樣靜靜地躺著,像睡著了一般案糙。 火紅的嫁衣襯著肌膚如雪限嫌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天时捌,我揣著相機(jī)與錄音怒医,去河邊找鬼。 笑死奢讨,一個(gè)胖子當(dāng)著我的面吹牛稚叹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拿诸,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼入录,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了佳镜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凡桥,失蹤者是張志新(化名)和其女友劉穎蟀伸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啊掏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蠢络,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迟蜜。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刹孔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娜睛,到底是詐尸還是另有隱情髓霞,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布畦戒,位于F島的核電站方库,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏障斋。R本人自食惡果不足惜纵潦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垃环。 院中可真熱鬧邀层,春花似錦、人聲如沸遂庄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涧团。三九已至只磷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泌绣,已是汗流浹背钮追。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阿迈,地道東北人元媚。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像苗沧,于是被迫代替她去往敵國(guó)和親刊棕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容