django-rest-framework学习之路-2-序列化


django-rest-framework学习之路-2-序列化

1、引入概念

我们想把一个对象封装后提供出一个接口给其他人用,根据约定好的格式进行数据传递,比如使用Json。

例如

import pprint
class Student:
    def __init__(self, stu_id, name, age):
        self.stu_id = stu_id
        self.name = name
        self.age = age
    def to_json(self):
        info = dict(stu_id=self.stu_id, name=self.name, age=self.age)
        pprint.pprint(info)
        return info
if __name__ == '__main__':
    student = Student(10086, '墨玉麒麟', 18)
    student_info = student.to_json()
    print(f"学生的信息是:{student_info}")

同理,我们也要约定一种格式,在django之间数把数据传递给其他人用,因此,我们需要序列化代码片段,上面的做法就是使用json序列化Student对象,来进行数据之间的传递。

在django-rest-framework中,我们使用serializers库来进行数据的序列化,可以理解为这是django-rest-framework的约定。

2、创建一个序列化类

在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

说明

  • 序列化器类的第一部分定义了序列化/反序列化的字段。create()和update()方法定义了在调用serializer.save()时如何创建和修改完整的实例。
  • 序列化器类与Django Form类非常相似,并在各种字段中包含类似的验证标志,例如required,max_length和default。
  • 字段标志还可以控制serializer在某些情况下如何显示,比如渲染HTML的时候。上面的{'base_template': 'textarea.html'}标志等同于在Django Form类中使用widget=widgets.Textarea。这对于控制如何显示可浏览器浏览的API特别有用,我们将在本教程的后面看到。
  • 我们实际上也可以通过使用ModelSerializer类来节省时间,就像我们后面会用到的那样。但是现在我们还继续使用我们明确定义的serializer。

3、熟悉序列化类

我们进入django的shell中,来熟悉一下我们刚才新建的序列化类SnippetSerializer

使用刚才的cmd窗口(当然也可以新开一个窗口,只要保证目录在我们的项目目录下即可)

运行命令,进入shell交互

python manage.py shell

先来创建两个Snippet对象,然后保存到数据库

from snippets.models import Snippet
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print "hello, world"\n')
snippet.save()

这样数据库就保存了两个Snippet对象,也就是两条数据库记录

数据库文件就是项目目录下的db.sqlite3,使用任意一个sqlite可视化工具都可以查看它,例如我用的是SQLiteStudio

接下来就是序列化刚才创建的Snippet对象

from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer(snippet)
serializer.data
print(type(serializer.data))

把序列化后的数据转成bytes流

from rest_framework.renderers import JSONRenderer
content = JSONRenderer().render(serializer.data)
print(content)
print(type(content))

当然也可以把流形式反序列化回来

from io import BytesIO
from rest_framework.parsers import JSONParser
stream = BytesIO(content)
data = JSONParser().parse(stream)
serializer = SnippetSerializer(data=data)
serializer.is_valid()
serializer.data

我们也可以序列化所有的Snippet对象,而不是只序列化一个,通过参数many=True即可

serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data

4、使用模型序列化

之所以SnippetSerializer类能序列化Snippet对象,是因为我们定义的SnippetSerializer类和Snippet很相似,可以说是把Snippet对象的属性搬过来了一份,我们想要序列化哪一个属性,我们就搬哪一个属性过来,有点映射的味道了吧。

那么我们是否可以直接使用Snippet模型对象类构建SnippetSerializer序列化类呢,那当然是必然的,我们可以这样子构建

4.1、修改snippets/serializers.py文件内容为如下内容

from rest_framework import serializers
from snippets.models import Snippet


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

4.2、可以通过repr来查看序列化类序列化了哪些字段

from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))

还是在cmd中查看一下

别慌,后面这些乱七八糟的是language的选项而已,同时也能看到style的选项

5、使用序列化类编写视图函数

接下来,我们通过序列化类进行视图的编写

在snippets/views.py文件中添加如下代码

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from rest_framework.renderers import JSONRenderer

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


class JSONResponse(HttpResponse):
    """
    An HttpResponse that renders its content into JSON.
    """

    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)  # 把序列化对象转为bytes流
        print(type(content))
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)


@csrf_exempt
def snippet_list(request):
    """
    列出所有的code snippet,或创建一个新的snippet。
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JSONResponse(serializer.data)

    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):
    """
    获取,更新或删除一个 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)
  • snippet_list视图支持列出所有现有的snippet或创建一个新的snippet。

  • snippet_detail视图,并且用于获取,更新和删除这个snippet。

我们需要添加对应的路由

创建一个snippets/urls.py文件,添加如下代码

from django.urls import re_path

from snippets import views

urlpatterns = [
    re_path(r'^snippets/$', views.snippet_list),
    re_path(r'^snippets/(?P[0-9]+)/$', views.snippet_detail),
]

把snippets app的路由注册到tutorial/urls.py中

在tutorial/urls.py添加如下代码

from django.urls import include
path('snippets/', include('snippets.urls')),

6、启动服务,查看序列化效果

退出刚才的cmd或者直接关闭

启动服务

python manage.py runserver

打开浏览器,查看效果

地址:http://127.0.0.1:8000/snippets/snippets/

地址:http://127.0.0.1:8000/snippets/snippets/1/

当然,这两个都是get的用法

使用Apipost模拟调试

  • 获取所有的Snippet对象
curl --location --request GET --X GET 'http://127.0.0.1:8000/snippets/snippets' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/'

  • 创建一个Snippet对象
curl --location --request POST --X POST 'http://127.0.0.1:8000/snippets/snippets/' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/' \
--header 'Content-Type: application/json' \
--data '	{
		"title": "测试1",
		"code": "foo = \"bar1\"\n",
		"linenos": false,
		"language": "python",
		"style": "friendly"
	}'

数据库

  • 获取一个Snippet对象
curl --location --request GET --X GET 'http://127.0.0.1:8000/snippets/snippets/3' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/'

  • 修改一个Snippet对象
curl --location --request PUT --X PUT 'http://127.0.0.1:8000/snippets/snippets/3/' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/' \
--header 'Content-Type: application/json' \
--data '{
	"title": "测试1更名",
	"code": "foo = \"bar1-更名\"",
	"linenos": false,
	"language": "python",
	"style": "friendly"
}'

数据库

  • 删除一个Snippet对象
curl --location --request DELETE --X DELETE 'http://127.0.0.1:8000/snippets/snippets/3/' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/'

数据库

学习链接:https://www.w3cschool.cn/lxraw/lxraw-8kq335ob.html