教程1:序列化

介紹

本教程將介紹如何創建突出顯示Web API的簡單pastebin代碼。在此過程中,它將介紹組成REST framework的各種組件,並讓您全面瞭解所有內容如何組合在一起。

本教程相當深入,因此在開始之前,您可能應該先獲得餅乾和一杯自己喜歡的啤酒。如果您只想快速瀏覽一下,則應該轉到快速入門文檔。


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


設置新環境

在執行其他任何操作之前,我們將使用venv創建一個新的虛擬環境。這將確保我們的程序包配置與我們正在處理的任何其他項目保持良好的隔離。

python3 -m venv env

# Linux系統啓動虛擬環境
source env/bin/activate

# Windows系統啓動虛擬環境
env\Scripts\activate

現在我們處於虛擬環境中,我們可以安裝軟件包要求。

pip install django
pip install djangorestframework
pip install pygments  # 我們將用它來高亮顯示代碼

注意: 要隨時退出虛擬環境,只需鍵入deactivate。有關更多信息,請參閱venv文檔

入門

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

cd ~
django-admin startproject tutorial
cd tutorial

完成後,我們可以創建一個應用程序,用它來創建一個簡單的Web API。

python manage.py startapp snippets

我們需要將我們的新snippets應用程序和該rest_framework應用程序添加到中INSTALLED_APPS。讓我們編輯tutorial/settings.py文件:

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

好的,我們準備開始了。

創建要使用的模型

在本教程中,我們將從創建一個Snippet用於存儲代碼段的簡單模型開始。繼續編輯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, 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

創建一個序列化器類 Serializer

我們需要開始使用Web API的第一件事是提供一種將片段實例序列化和反序列化爲諸如的表示形式的方法json。我們可以通過聲明與Django形式非常相似的序列化器來做到這一點。在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

序列化程序類的第一部分定義了要進行序列化/反序列化的字段。該create()update()方法定義實例如何完全成熟的創建或修改時調用serializer.save()

串行類非常類似於一個Django Form類,並且包括關於各個字段類似的驗證標記,如requiredmax_lengthdefault

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

實際上,我們也可以使用ModelSerializer該類節省一些時間,我們將在後面看到,但是現在我們將使序列化程序定義保持明確。

使用序列化器 Serializer

在繼續之前,我們將使我們熟悉使用新的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
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}

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

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

反序列化是相似的。首先,我們將流解析爲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
# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

使用ModelSerializer

我們的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']

序列化程序具有的一個不錯的屬性是,您可以通過打印序列化程序的表示形式來檢查序列化程序實例中的所有字段。使用打開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類並沒有做任何特別神奇的事情,它們只是創建序列化器類的捷徑:

  • 自動確定的一組字段。
  • 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):
    """
    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令牌的客戶端發佈到該視圖,因此需要將該視圖標記爲csrf_exempt。這通常不是您要做的,並且REST框架視圖實際上比這更明智的行爲,但是現在出於我們的目的。

我們還將需要一個與單個代碼段相對應的視圖,該視圖可用於檢索,更新或刪除該代碼段。

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

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

我們還需要連接tutorial/urls.py文件中的根urlconf ,以包含代碼段應用程序的URL。

from django.urls import path, include

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

值得注意的是,目前有一些邊緣案例我們無法正確處理。如果我們發送格式錯誤的json,或者使用該視圖無法處理的方法發出請求,那麼最終將收到500個“服務器錯誤”響應。不過,這將暫時執行。

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

現在,我們可以啓動一個提供摘要的示例服務器。

退出Django 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部分中,我們將看到如何開始改進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章