[django-rest-framework]01.Serialization,序列器!

本人普通一本,正值大三,爲了能有好的就業,痛定思痛戒掉遊戲!鞭策自己至少一週一篇博客

  • 學習目標:學校課內基礎打紮實,課外學會語言框架,同時提升英語閱讀水平
  • 學習路線(自擬):vue -> djangorestframework -> goland
  • 畢業前想:用前後端分離建成自己的小站,做一款網絡遊戲(愛好而已)

關於本篇djangorestframwork

內容皆會上傳到我的github上:https://github.com/BadbadLoli/django-rest-framework-study

本篇參照官網教程:https://www.django-rest-framework.org/tutorial/1-serialization/

學習爲主,如有紕漏請大神指正


介紹

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

項目準備

創建一個新項目tutorial,並創建一個新的app snippets

django-admin startproject tutorial
...
python manage.py startapp snippets

然後我們需要告訴django我們要使用snippetsrestframework

./tutorial/settings.py

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

創建model

現在,我們創建一個簡單的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']

pygments是一個優化markdown,對其中的代碼塊進行高亮的庫,不需要做深入瞭解

模型創建完之後要進行數據遷移

python manage.py makemigrations
...
python manage.py migrate

創建一個Serializer

現在,我們需要在Web API上把snippet實例序列化和反序列化爲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):
        # 創建返回一個新的Snippet實例,並附上驗證好的數據
        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

serializer的第一部分是定義需要序列化/反序列化的字段,方法create()和update()則定義了在調用save()方法時如何創建或修改實例

serializer與Django表單類非常相似,並且在各個字段上包含類似的驗證標誌,比如required、max_length和default

當然,我們也可以用ModelSerializer類來節省時間,跟django中的表單類ModelForm類一樣

./snippets/serializers.py

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

要記住,ModelSerializer類只是創建serializer的快捷方式,並沒有什麼神奇的功能,像create()update()方法只是默認實現

使用python manage.py shell 打開django shell然後嘗試以下操作:

  1. 我們要創建一些實例來使用
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()
  1. 我們現在可以來看看序列化其中一個實例
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
  1. 我們還可以序列化queryset而不是模型實例。爲此,我們只需向序列化器參數添加一個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')])]

編寫視圖View

讓我們看看如何使用serializer來編寫一些API視圖,目前,我們編寫的視圖爲普通的django視圖

./snippet/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


@csrf_exempt
def snippet_list(request):
    # 展示(GET)所有snippet,或者創建(POST)一個新的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_exempt
def snippet_detail(request, pk):
    # 檢索,更新或刪除一個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)

注意,由於我們希望能夠從沒有CSRF令牌的客戶端向上面的視圖發送POST請求,所以我們要給視圖函數加上@csrf_exempt的裝飾器,其實drf視圖實際上有比這更合理的行爲,但現在爲了我們的教學目的,就先這樣做吧

最後,我們需要將這些視圖連接起來

./snippet/urls.py

from django.urls import path
from snippets import views

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

我們還要連接根url

./tutorial/urls.py

from django.urls import path, include

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

值得注意的是,有幾個邊緣情況我們目前沒有正確處理。如果我們發送格式不正確的json,或者發送的請求視圖無法處理,那麼我們將得到一個500的“服務器錯誤”響應。不過,現在這樣就可以了。

測試一下我們的web API

python manage.py runserver

我用的postman進行測試

到目前爲止,我們做得還不錯,我們有一個序列化API,感覺非常類似於Django的表單API,以及一些常規的Django視圖。
目前,我們的API視圖除了提供json響應之外,沒有做任何特別的事情,我們仍然希望清理一些錯誤處理的邊緣情況,但它是一個功能良好的Web API。

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

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