[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部分中看到如何开始改进。

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