序列化 Serialization

安装咱们依赖的包
pip install pygments # 代码高亮插件

开始

咱们就能够建立一个应用,咱们将会用他来建立简单的Web API。html

python manage.py startapp snippets

添加一个新的snippets应用和rest_framework应用到INSTALLED_APPS。让咱们编辑tutorial/settings.py文件:python

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

 

.web

建立一个 Model

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',)

为咱们的snippet模型建立一个初始迁移(initial migration),而后第一次同步数据库。shell

 

python manage.py makemigrations snippets
python manage.py migrate

建立一个序列化类(Serializer class)

着手咱们的Web API,首先要作的是,提供一种将咱们的snippet实例序列化/反序列化成例如json这样的表述形式。咱们能够经过声明序列来完成,这些序列与Django的表单(forms)工做类似。在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

序列化类(serializer class)的第一部分定义了一些须要被序列化/反序列化字段。create()update()方法定义了在调用serializer.save()时成熟的实例是如何被建立和修改的。 序列化类(serializer class)与Django的表单类(Form class)很是类似,包括对各类字段有类似的确认标志(flag),例如requiredmax_lengthdefault。 在某些状况下,这些字段标志也能控制序列应该怎么表现,例如在将序列渲染成HTML时。{'base_template': 'textarea.html}'标志至关于对Django表单(Form)类使用widget=widgets.Textarea。这对控制API的显示尤为有用,之后的教程将会看到。 事实上,之后咱们能够经过使用ModelSerializer类来节约咱们的时间,可是如今为了让咱们序列化定义更清晰,咱们用Serializer类。django

用序列化(Serializers)工做

在咱们深刻以前,咱们须要熟练使用新的序列化列(Serializer class)。然咱们开始使用Django命令行吧。json

python manage.py shell

如今咱们已经有了一些snippet实例。让咱们看看如何将其中一个实例序列化。api

serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}

如今,咱们已经将模型实例(model instance)转化成Python原生数据类型。为了完成实例化过程,咱们要将数据渲染成json。浏览器

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

反序列化也同样。首先,咱们须要将流(stream)解析成Python原生数据类型...服务器

from django.utils.six import BytesIO

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

...而后咱们要将Python原生数据类型恢复成正常的对象实例。

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和表单(forms)是多么类似啊。当咱们用咱们的序列写视图的时候,类似性会至关明显。 除了将模型实例(model instance)序列化外,咱们也能序列化查询集(querysets),只须要添加一个序列化参数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类复制了包含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')

 

序列一个很是棒的属性就是,你可以经过打印序列实例的结构(representation)查看它的全部字段。输入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视图

让咱们看看,使用咱们新的序列化类,咱们怎么写一些API视图。此刻,咱们不会使用REST框架的其余特性,仅仅像写常规Django视图同样。 经过建立HttpResponse的一个子类来开始,其中,咱们能够用这个子类来渲染任何咱们返回的json数据。 编辑snippets/views.py文件,添加如下代码。

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

 

咱们的根API将是一个支持列出全部存在的snippets的视图,或者建立一个新的snippet对象。

复制代码
@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 token的客户端POST数据到这个视图,咱们须要标记这个视图为csrf_exempt。一般,你并不想这么作,而且事实上REST框架视图更实用的作法不是这样的,可是目前来讲,这足以到达咱们的目的。 咱们也须要一个与单个snippet对象相应的视图,而且咱们使用这个视图来读取、更新或者删除这个snippet对象。

复制代码
@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.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),
]
复制代码

 

咱们也须要在根url配置文件tutorial/urls.py中添加咱们的snippet应用URL。

from django.conf.urls import url, include

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

 

有一些当时咱们没有正确处理的边缘事件是没有价值的。若是咱们发送不正确的json数据,或者若是咱们制造了一个视图没有写处理的方法(method),那么咱们会获得500“服务器错误”的响应。固然,如今也会出现这个问题。

测试咱们Web API的第一次努力

如今咱们开始建立一个测试服务器来服务咱们的snippets应用。 退出命令行......

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.
复制代码

 

咱们能够在另外一个终端测试服务器。 咱们能够用curl和httpie来测试咱们的API。Httpie是一个面向用户的很是友好的http客户端,它是用Python写的。让咱们来安装它。 你能够经过pip来安装httpie:

pip install httpie

 

最后,咱们来获取一个包含全部snippets的列表:

复制代码
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来获取指定的snippet:

复制代码
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"
}
复制代码

 

类似地,你能够经过在浏览器中访问这些连接来得到相同的json数据。

咱们如今在哪

到目前为止,咱们作的都很好,咱们已经得到一个序列化API,这和Django的表单API很是类似,而且咱们写好了一些经常使用的Django视图。 如今,咱们的API视图除了服务于json外,不会作任何其余特别的东西,而且有一些错误咱们仍然须要清理,可是它是一个可用的Web API。


扫码关注微信公众号  “小樊Study”获取更多

 

直男们,扫我送女朋友哟!

相关文章
相关标签/搜索