教程1-序列化

序列化

介紹

本教程將介紹如何建立一個簡單的 pastebin 代碼突出顯示webapi。在此過程當中,它將介紹組成REST框架的各類組件,並全面瞭解全部內容如何組合在一塊兒。html

本教程至關深刻,所以,在開始以前,您可能應該先得到餅乾和一杯本身喜歡的啤酒。若是您只想快速瀏覽一下,則應該轉到快速入門文檔。python

注意: 該教程的代碼可在GitHub上的encode/rest-framework-tutorial存儲庫中找到。完整的實施也能夠做爲沙箱版本在線進行測試,可在此處得到。web

設置新環境

在進行其餘操做以前,咱們將使用 venv建立一個新的虛擬環境,這將確保咱們的程序包配置與咱們正在處理的任何其餘項目保持良好的隔離。shell

python3 -m venv env
source env/bin/activate

如今咱們處於虛擬環境中,咱們能夠安裝軟件包要求。數據庫

pip install django
pip install djangorestframework
pip install pygments  # 咱們將用它來突出顯示代碼

注意: 輸入deactivate隨時退出虛擬環境,有關更多信息,請參閱 venv文檔django

入門

好的,咱們已經準備好進行編碼。首先,讓咱們建立一個要使用的新項目。編程

完成後,咱們能夠建立一個應用程序,用它來建立一個簡單的Web API。json

python manage.py startapp snippetsapi

咱們須要將新的snippets應用和rest_framework應用添加到INSTALLED_APPS。 讓咱們編輯 tutorial/settings.py文件:瀏覽器

INSTALLED_APPS = [
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
]

好的,咱們準備開始了。

建立一個可使用的模型

在本教程中,咱們將從建立一個簡單的Snippet模型開始,該模型用於存儲代碼片斷.繼續編輯snippits/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):
    """
    使用`pygments`庫建立突出顯示的HTML代碼段的表示形式。
    """
    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

建立一個序列化器類

咱們須要開始使用Web API的第一件事是提供一種將片斷實例序列化和反序列化爲json等表示形式的方法。咱們能夠經過聲明與Django形式很是類似的序列化器來實現此目的。在snippets目錄中建立一個名爲seralizers.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):
        """
        給定通過驗證的數據,更新並返回一個現有的「 Snippet」實例。
        """
        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()時如何建立或修改完整實例。

序列化器類與Django Form類很是類似。而且在各個字段上都包含相似的驗證標誌,例如requiredmax_lengthdefault

字段標誌還能夠控制在某些狀況下(例如,呈現爲HTML時)應如何顯示序列化程序。上面的{'base_template':'textarea.html'}標誌等效於在Django Form類上使用widget = widgets.Textarea。這對於控制應如何顯示可瀏覽的API尤爲有用,咱們將在本教程的後面部分看到。

實際上,咱們也能夠經過使用ModelSerializer類節省一些時間,咱們將在後面看到,可是如今咱們將使序列化程序定義保持明確。

使用序列化器

在繼續以前,咱們將熟悉使用新的Serializer類。讓咱們進入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

至此,咱們已經將模型實例轉換爲Python本機數據類型。爲了完成序列化過程,咱們將數據渲染到json中。

反序列化是類似的。 首先,咱們將流解析爲Python本機數據類型。

import io

stream = io.BytesIO(content)
data = JSONParser().parse(stream)

而後咱們將這些本機數據類型恢復到徹底填充的對象實例中。

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

使用 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']

序列化程序有一個很好的特性,就是能夠檢查序列化程序實例中的全部字段,經過打印其表示。使用python manage.py shell打開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()方法的簡單默認實現。

使用序列化器編寫常規Django視圖

讓咱們看看如何使用新的Serializer類編寫一些API視圖。目前,咱們不會使用REST框架的其餘任何功能,咱們將這些視圖編寫爲常規Django視圖。

編輯snippets/views.py文件,而後添加如下內容:

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

咱們的API的根源將是一個支持列出全部現有代碼段或建立新代碼段的視圖。

@csrf_exempt
def snippet_list(request):
    """
    列出全部snippet,或建立一個新的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令牌的客戶那裏發佈到這個視圖,咱們須要將該視圖標記爲 csrf_exempt.這不是您一般想要作的,REST框架視圖實際上比這使用了更明智的行爲,不過,如今就能夠知足咱們的目的了。

咱們還須要一個與單個片斷相對應的視圖,並能夠用來檢索,更新或刪除代碼段。

@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.urls import path
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]

咱們還須要鏈接根urlconf, 在torial/urls.py文件中,包含咱們的代碼片斷應用程序的URL。

from django.urls import path, include

urlpatterns = [
    path('', include('snippets.urls')),
]

值得注意的是,咱們目前尚未妥善處理一些邊緣狀況。若是咱們發送格式錯誤的 json.,或者若是使用視圖沒法處理的方法發出請求,那麼咱們將獲得500 的 「服務器錯誤」響應。不過,如今這樣就好了。

測試咱們第一次嘗試使用Web API

如今,咱們能夠啓動一個示例服務器,以服務於咱們的片斷,

先退出 shell。

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.

在另外一個終端窗口中,咱們能夠測試服務器。

咱們可使用curlhttpie測試咱們的API。Httpie是使用Python編寫的用戶友好型HTTP客戶端。讓咱們安裝它。

您可使用pip安裝httpie:

pip install httpie

最後,咱們能夠得到全部代碼段的列表:

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來獲取它:

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"
}

一樣,經過在Web瀏覽器中訪問這些URL,能夠顯示相同的json.

咱們如今在哪?

到目前爲止咱們還好,咱們有一個序列化API,感受與Django的Forms API很是類似,以及一些常規的Django視圖。

除了提供json響應外,咱們的API視圖目前並無作任何特別的事情,而且咱們仍然但願清理一些錯誤處理的極端狀況,但這是一個正常運行的Web API。

在本教程的第2部分中,咱們將看到如何開始改進。

相關文章
相關標籤/搜索