• Django-Rest-Framework 教程: 1. 序列化 (Serialization)


    在本篇中, 我们将通过建立一个代码黏贴板(pastebin), 来熟悉组成REST framework的各组成部分, 并了解这些部件是如何相互协调工作的.

    1. 环境设置

    首先我们使用virtualenvwrapper创建新的virtualenv, 并安装需要的代码库:

        mkvirtualenv env
        pip install django
        pip install djangorestframework
        pip install pygments # 用于代码高亮

    2. Django项目设置

    我们建立Django项目tutorial, 和app snippets

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

    设置settings.py:

        # tutorial/settings.py
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.postgresql_psycopg2',
                'NAME': 'database_name',
                'USER': 'database_user',
                'PASSWORD': 'database_password',
                'HOST': '',
                'PORT': ''
            }
        }
    
        INSTALLED_APPS = (
        ...
        'rest_framework',
        'snippets',
        )

    设置urls.py, 将新建的snippet app中的urls.py加入到其中:

        # tutorial/urls.py
        urlpatterns = patterns('',
            url(r'^', include('snippets.urls')),
        )

    3. 创建Model

    我们建立Snippet Model用于储存代码:

        # snippet/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',)

    启动django服务器:

        python manage.py syncdb

    4. 创建Serializer

    第一步我们需要为我们的API提供序列化和反序列化的方法, 将snippet实例转为json等方式呈现数据. 我们可以使用Serializer达到这一目的, Serializer和django forms十分相似. 我们建立snippet/serializers.py文件:

        # snippets/serializers.py
        from django.forms import widgets
        from rest_framework import serializers
        from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
    
    
        class SnippetSerializer(serializers.Serializer):
            pk = serializers.Field()  # `Field` 是无类型, 只读的.
            title = serializers.CharField(required=False,
                                          max_length=100)
            code = serializers.CharField(widget=widgets.Textarea,
                                         max_length=100000)
            linenos = serializers.BooleanField(required=False)
            language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
                                               default='python')
            style = serializers.ChoiceField(choices=STYLE_CHOICES,
                                            default='friendly')
    
            def restore_object(self, attrs, instance=None):
                """
                创建或更新一个snippet实例, 返回该snippet实例
    
                如果不定义该function, 则反序列化时将返回一个包括所有field的dict
                """
                if instance:
                    # 更新已存在的snippet实例
                    instance.title = attrs.get('title', instance.title)
                    instance.code = attrs.get('code', instance.code)
                    instance.linenos = attrs.get('linenos', instance.linenos)
                    instance.language = attrs.get('language', instance.language)
                    instance.style = attrs.get('style', instance.style)
                    return instance
    
                # Create new instance
                return Snippet(**attrs)

    以上代码第一部分定义了序列化和反序列化的项, 第二部分restore_object function则定义了符合要求的已序列化snippet实例如何反序列化.

    注意, 我们也可以使用在django form中使用的参数, 比如widget=widgets.Textarea. 这些参数可以控制serializer如何显示为HTML form, 尤其是在构建可浏览的API时, 十分有用, 我们也会在今后的博文中详细介绍.

    如果想快速的构建serializer, 我们也可以使用ModelSerializer, 我们会在稍后介绍:

    5. 使用serializer

    在继续完成该项目前, 我们先熟悉一下serializer, 是有manage.py shell启动django shell:

        python manage.py shell

    import必要的代码库并创建2个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"
    ')
        snippet.save()
    
        snippet = Snippet(code='print "hello, world"
    ')
        snippet.save()

    序列化其中一个实例:

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

    以上代码已将snippet实例转化为Python基本数据类型, 接下来我们完成序列化:

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

    反序列化也是类似的, 首先将stream转为python基本类型:

        # 根据我们使用的是 python 2 或是 python 3
        # 这一import会自动引入 `StringIO.StringIO` 或 `io.BytesIO`
        from rest_framework.compat import BytesIO
    
        stream = BytesIO(content)
        data = JSONParser().parse(stream)

    然后我们将它转化为snippet实例:

        serializer = SnippetSerializer(data=data)
        serializer.is_valid()
        # True
        serializer.object
        # <Snippet: Snippet object>

    可见, serializer和django form 有多么相似, 当我们写view时, 这一相似性会更加明显.

    当我们输入参数many=True时, serializer还能序列化queryset:

        serializer = SnippetSerializer(Snippet.objects.all(), many=True)
        serializer.data
        # [{'pk': 1, 'title': u'', 'code': u'foo = "bar"
    ', 'linenos': False, 'language': u'python',
        'style': u'friendly'}, {'pk': 2, 'title': u'', 'code': u'print "hello, world"
    ', 'linenos': False,
        'language': u'python', 'style': u'friendly'}]

    6. 使用 ModelSerializers

    在我们的SnippetSerializer中, 包含了许多与model重复的field, 那么是否能将这些代码变得更紧凑呢? 当然可以!

    就像Django提供ModelForm一样, django_rest_framework提供了ModelSerializer.

    我们修改之前的SnippetSerializer:

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

    7. 在Views中使用Serializer

    以下代码中, 为了更好的理解Serializer, 我们只使用django_rest_framework的Serializer部件和Django最基本的function_base_view.

    首先创建能返回json数据的HttpResponse:

        # snippets/views.py
        from django.http import HttpResponse
        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
    
        class JSONResponse(HttpResponse):
            """
            将内容转为JSON格式的HttpResponse
            """
            def __init__(self, data, **kwargs):
                content = JSONRenderer().render(data)
                kwargs['content_type'] = 'application/json'
                super(JSONResponse, self).__init__(content, **kwargs)

    我们API的根目录是一个list view, 用于展示所有存在的snippet, 或建立新的snippet:

        # snippets/views.py
        @csrf_exempt
        def snippet_list(request):
            """
            展示所有存在的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)

    注意, 为了简便, 我们希望在POST时不使用csrf, 因此使用了csrf_exempt. 这不是通常应该做的, 而且django_rest_framework默认使用了更为安全的方式.

    用于展示, 更新或删除的view:

        # snippets/views.py
        @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)

    最后修改urls.py, 使这些view通电

        # snippets/urls.py
        from django.conf.urls import patterns, url
    
        urlpatterns = patterns('snippets.views',
            url(r'^snippets/$', 'snippet_list'),
            url(r'^snippets/(?P<pk>[0-9]+)/$', 'snippet_detail'),
        )

    需要注意的是, 我们还有许多错误处理没有涉及, 例如提交错误的json, 使用view不支持的http method等. 同样处于更好的理解serializer的目的, 这些错误都会返回500错误页.

    8. 测试

    首先我们推出shell:

        quit()

    启动django server:

        python manage.py runserver

    在另外的终端中:

    curl http://127.0.0.1:8000/snippets/
    
    [{"id": 1, "title": "", "code": "foo = "bar"
    ", "linenos": false, "language": "python", "style":
        "friendly"}, {"id": 2, "title": "", "code": "print "hello, world"
    ", "linenos": false, "language":
        "python", "style": "friendly"}]

    或者我们使用id获取一个snippet:

        curl http://127.0.0.1:8000/snippets/2/
    
        {"id": 2, "title": "", "code": "print "hello, world"
    ", "linenos": false,
        "language": "python", "style": "friendly"}

    同样, 我们也可以使用浏览器测试这些API.

    原文链接: http://www.weiguda.com/blog/19/

  • 相关阅读:
    react 之 ref
    再看redux
    localtunnel内网服务器暴露至公网
    Relay GraphQL理解
    微信小程序
    React Router
    webpack
    Redux
    bootstrap
    jQuery中.bind() .live() .delegate() .on()区别
  • 原文地址:https://www.cnblogs.com/leo23/p/5051905.html
Copyright © 2020-2023  润新知