序列化 Serialization

安裝咱們依賴的包
pip install pygments # 代碼高亮插件

開始

咱們就能夠建立一個應用,咱們將會用他來建立簡單的Web API。html

python manage.py startapp snippets

添加一個新的snippets應用和rest_framework應用到INSTALLED_APPS。讓咱們編輯tutorial/settings.py文件:python

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

 

.web

建立一個 Model

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',)

爲咱們的snippet模型建立一個初始遷移(initial migration),而後第一次同步數據庫。shell

 

python manage.py makemigrations snippets
python manage.py migrate

建立一個序列化類(Serializer class)

着手咱們的Web API,首先要作的是,提供一種將咱們的snippet實例序列化/反序列化成例如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):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the 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 class)的第一部分定義了一些須要被序列化/反序列化字段。create()update()方法定義了在調用serializer.save()時成熟的實例是如何被建立和修改的。 序列化類(serializer class)與Django的表單類(Form class)很是類似,包括對各類字段有類似的確認標誌(flag),例如requiredmax_lengthdefault。 在某些狀況下,這些字段標誌也能控制序列應該怎麼表現,例如在將序列渲染成HTML時。{'base_template': 'textarea.html}'標誌至關於對Django表單(Form)類使用widget=widgets.Textarea。這對控制API的顯示尤爲有用,之後的教程將會看到。 事實上,之後咱們能夠經過使用ModelSerializer類來節約咱們的時間,可是如今爲了讓咱們序列化定義更清晰,咱們用Serializer類。django

用序列化(Serializers)工做

在咱們深刻以前,咱們須要熟練使用新的序列化列(Serializer class)。然咱們開始使用Django命令行吧。json

python manage.py shell

如今咱們已經有了一些snippet實例。讓咱們看看如何將其中一個實例序列化。api

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

如今,咱們已經將模型實例(model instance)轉化成Python原生數據類型。爲了完成實例化過程,咱們要將數據渲染成json。瀏覽器

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

反序列化也同樣。首先,咱們須要將流(stream)解析成Python原生數據類型...服務器

from django.utils.six import BytesIO

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和表單(forms)是多麼類似啊。當咱們用咱們的序列寫視圖的時候,類似性會至關明顯。 除了將模型實例(model instance)序列化外,咱們也能序列化查詢集(querysets),只須要添加一個序列化參數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')])]
 

使用模型序列化ModelSerializers

咱們的SnippetSerializer類複製了包含Snippet模型在內的不少信息。若是咱們能簡化咱們的代碼,那就更好了。 以Django提供表單(Form)類和模型表單(ModelForm)類相同的方式,REST 框架包括了實例化(Serializer)類和模型實例化(ModelSerializer)類。 咱們來看看用ModelSerializer類建立的序列。再次打開snippets/serializers.py文件,用下面的代碼重寫SnippetSerializer類。

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

 

序列一個很是棒的屬性就是,你可以經過打印序列實例的結構(representation)查看它的全部字段。輸入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類並無作什麼有魔力的事情,它們僅僅是一個建立序列化類的快捷方式。

  • 一個自動決定的字段集合。
  • 簡單的默認create()update()方法的實現。

用咱們的序列化來寫常規的Django視圖

讓咱們看看,使用咱們新的序列化類,咱們怎麼寫一些API視圖。此刻,咱們不會使用REST框架的其餘特性,僅僅像寫常規Django視圖同樣。 經過建立HttpResponse的一個子類來開始,其中,咱們能夠用這個子類來渲染任何咱們返回的json數據。 編輯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將是一個支持列出全部存在的snippets的視圖,或者建立一個新的snippet對象。

複製代碼
@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, 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 token的客戶端POST數據到這個視圖,咱們須要標記這個視圖爲csrf_exempt。一般,你並不想這麼作,而且事實上REST框架視圖更實用的作法不是這樣的,可是目前來講,這足以到達咱們的目的。 咱們也須要一個與單個snippet對象相應的視圖,而且咱們使用這個視圖來讀取、更新或者刪除這個snippet對象。

複製代碼
@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)
複製代碼

 

最終,咱們須要用線將這些視圖連起來。建立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),
]
複製代碼

 

咱們也須要在根url配置文件tutorial/urls.py中添加咱們的snippet應用URL。

from django.conf.urls import url, include

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

 

有一些當時咱們沒有正確處理的邊緣事件是沒有價值的。若是咱們發送不正確的json數據,或者若是咱們製造了一個視圖沒有寫處理的方法(method),那麼咱們會獲得500「服務器錯誤」的響應。固然,如今也會出現這個問題。

測試咱們Web API的第一次努力

如今咱們開始建立一個測試服務器來服務咱們的snippets應用。 退出命令行......

quit()

 

...而後啓動Django開發服務器。

複製代碼
python manage.py runserver

Validating models...

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

 

咱們能夠在另外一個終端測試服務器。 咱們能夠用curl和httpie來測試咱們的API。Httpie是一個面向用戶的很是友好的http客戶端,它是用Python寫的。讓咱們來安裝它。 你能夠經過pip來安裝httpie:

pip install httpie

 

最後,咱們來獲取一個包含全部snippets的列表:

複製代碼
http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
...
[
  {
    "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"
  }
]
複製代碼

或者咱們能夠經過id來獲取指定的snippet:

複製代碼
http http://127.0.0.1:8000/snippets/2/

HTTP/1.1 200 OK
...
{
  "id": 2,
  "title": "",
  "code": "print \"hello, world\"\n",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}
複製代碼

 

類似地,你能夠經過在瀏覽器中訪問這些連接來得到相同的json數據。

咱們如今在哪

到目前爲止,咱們作的都很好,咱們已經得到一個序列化API,這和Django的表單API很是類似,而且咱們寫好了一些經常使用的Django視圖。 如今,咱們的API視圖除了服務於json外,不會作任何其餘特別的東西,而且有一些錯誤咱們仍然須要清理,可是它是一個可用的Web API。


掃碼關注微信公衆號  「小樊Study」獲取更多

 

直男們,掃我送女朋友喲!

相關文章
相關標籤/搜索