test: 博客美化中……

django-rest-framework之序列化

前言:昨天学习了rest-framework序列化方面的知识,故写了博客记录一下。官网:http://www.django-rest-framework.org/tutorial/1-serialization/#working-with-serializers。

 

前后端分离:前台的开发和后台的开发分离开。这个方案的实现就是要借助API,API简单说就是开发人员提供编程接口被其他人调用,他们调用之后会返回数据供其使用。API的类型有多种,但是现在比较主流且实用的就是RESTful API。

 

一、开始

Okay, we're ready to get coding. To get started, let's create a new project to work with.首先创建一个Django项目。

cd ~
django-admin.py startproject tutorial
cd tutorial

Once that's done we can create an app that we'll use to create a simple Web API.接着创建一个APP

python manage.py startapp snippets

We'll need to add our new snippets app and the rest_framework app to INSTALLED_APPS. Let's edit the tutorial/settings.py file:修改全局配置文件。

INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

Please note that if you're using Django <1.9, you need to replace snippets.apps.SnippetsConfig with snippets.

如果你使用的Django <1.9,则需要更换snippets.apps.SnippetsConfig为snippets

 

二、创建模型类

For the purposes of this tutorial we're going to start by creating a simple Snippet model that is used to store code snippets(片段). Go ahead and edit the snippets/models.py file.

我们将首先创建一个Snippet用于存储代码片段的简单模型,编辑snippets/models.py文件。

from django.db import models

# Create your models here.

# pygments可以实现代码高亮 官网:http://pygments.org/
from pygments.lexers import get_all_lexers  # 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

  

三、创建序列化类

The first thing we need to get started on our Web API is to provide a way of serializing(序列化) and deserializing(反序列化) the snippet instances into representations such as json. We can do this by declaring(声明) serializers that work very similar to Django's forms. 

首先我要问你了,通常情况下,前端从调用后台API,API肯定需要返回数据给前端,那返回的数据是什么类型呢?

目前主流用的比较多的是JSON,可能有少部分还在用XML。

那我们在学习django-rest-framework时,有没有提供什么方法能够将数据实例信息自动转换为JSON?而且也可以将前端传给后台的JSON数据转换为python的数据类型(dict/list/set...)。当然有,序列化器(serializers)可以实现。首先你得声明(引入)序列化器,请看下面这个例子:

# 用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)
    # 利用字段标志控制序列化器渲染到HTML页面时的的显示模板
    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):
        """
        The create() and update() methods define how fully fledged instances are created
        or modified when calling serializer.save()
        Create and return a new `Snippet` instance, given the validated data.
        给定经过验证的数据,创建并返回一个新的 Snippet 实例
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the 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()方法分别定义实例如何创建和更新。

关于下面这行代码:

code = serializers.CharField(style={'base_template': 'textarea.html'})

暂时需要知道的就是它的功能是控制序列化器渲染到HTML页面时的的显示模板,至于为什么要这样做,是因为这对于控制如何显示可浏览的API特别有用,这将在后面的文章中看到。

实际上也可以通过使用ModelSerializer来节省自己的时间,我们稍后会看到,接下来看看序列化器的具体作用。

 

四、使用序列化器

在我们进一步了解之前,我们将熟悉使用我们新的Serializer类。我们进入Django shell:

python manage.py shell

接下来的操作就和学习Django的ORM时那样,创建并保存Snippet模型实例:

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': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}

At this point we've translated the model instance into Python native datatypes. 

从上面的代码,可以看到,我们已经将model的实例信息转换为python原生的数据类型,即字典。

To finalize the serialization process we render the data into json.

为了完成序列化过程,我们将数据转换成json

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

这里已经出现了json格式,也就是说这个json格式的数据就是要展示在某个URL上,大概可以感觉到,等下我们在访问某个URL时,会返回上面这堆数据供你使用,这其实就完成了一个序列化的过程,也可以看出客户端的功能雏形。

序列化是为了返回json格式的数据给客户端查看和使用数据,那么当客户端需要修改、增加或者删除数据时,就要把过程反过来了,也就是反序列化,把客户端提交的json格式的数据反序列化。

反序列化是类似的。首先我们将一个流解析为Python本机数据类型...

from django.utils.six import BytesIO

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

...then we restore those native datatypes into a fully populated object instance.

检查数据是否有误,再保存数据:

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>

这个时候查看数据库又多了一条数据:

We can also serialize querysets instead of model instances. To do so we simply add a many=True flag to the serializer arguments.

我们也可以序列化查询集而不是模型实例。为此,我们只需要为serializer参数添加一个标志many=True

serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

 

 

五、使用 ModelSerializers

在上面的SnippetSerializer类中,我们继承的是serializers.Serializer类,可以看到SnippetSerializer类中有很多代码其实是和models.py中的Snippet模型类似一样的,所以这里我们可以改进一下。就像在Django中提供了Form类和ModelForm类一样,django-rest-framework为我们提供了Serializer类和ModelSerializer类。利用它可以让我们的代码简洁很多,修改serializers.py:

# Our SnippetSerializer class is replicating a lot of information that's also contained in the Snippet model.
# It would be nice if we could keep our code a bit more concise.
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

One nice property that serializers have is that you can inspect(检查/查看) all the fields in a serializer instance, by printing its representation. Open the Django shell with python manage.py shell, then try the following:

序列化器具有的一个不错的属性是可以通过打印其表示来检查序列化器实例中的所有字段。打开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视图。编辑snippets/views.py:

from django.shortcuts import render

# Create your views here.
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


@csrf_exempt
def snippet_list(request):
    """
    because we want to be able to POST to this view from clients
    that won't have a CSRF token we need to mark the view as csrf_exempt
    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_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)

上面的代码都比较好理解,定义了不同http动作时后台不同的操作,在这里也体现了restful API的理念。

because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as csrf_exempt.

因为我们希望能够从不具有CSRF令牌的客户端对此视图进行POST,因此我们需要给视图加个一个装饰器csrf_exempt

 

为了让视图函数被调用,那当然需要设计一下url了,这里的处理和平时Django开发时是一样的。首先创建snippets/urls.py

from django.conf.urls import url
from snippets import views

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

We also need to wire up the root urlconf, in the tutorial/urls.py file, to include our snippet app's URLs.

from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippets.urls')),
]

  

七、对API进行测试

完成了上面那些工作后,就可以开始测试了,退出shell模式并启动服务器,根据我们刚才设计的url发送请求,需要先安装httpie模块:

pip install httpie

然后在命令行窗口访问,效果如下: 

也可以访问指定id的数据: 

 当然了,也可以直接在浏览器查看,直接输入那个URL就可以了: 

 

posted @ 2017-11-01 14:31  前程明亮  阅读(3784)  评论(1编辑  收藏  举报