欢迎来访我的博客。

Django Rest Framework学习总结-1:序列化(Serialization)

Django 小张哥哥 337浏览 26评论

本文通过创建一个简单的,能高亮显示文本代码的工具的web api,介绍组成REST framework的哥哥组成部分。并对整个框架有一个全面的理解,了解它们是如何组成一体的。


设置新环境

我们首先使用venv创建一个新的虚拟环境,这可以保证我们的当前工作环境下的包配置和其它的项目隔离开来。

python3 -m venv 
envsource env/bin/activate

进入到虚拟环境中,我们需要安装一些依赖包。

pip install django
pip install djangorestframework
pip install pygments  # We'll be using this for the code highlighting

新建项目

现在可以开始我们的编码,我们创建一个新的项目:

cd ~
django-admin startproject tutorial
cd tutorial

在项目下,新建一个APP:

django-admin startapp snippets

我们还需要添加我们的app和rest_framework app到tutorial/setting.py文件的INSTALL_APPS中:

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

完成这些配置之后,我们可以正式开始了。


创建模型类

我们可以新建一个简单的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',)

创建好模型之后,我们同步一下数据库:

python manage.py makemigrations snippets
python manage.py migrate

创建序列化类

我们创建Web API的第一件事情就是要为我们的snippet实例提供序列化和反序列化的方法,将实例序列化为json等数据表现形式,或者从json等数据表现形式反序列化为实例。
我可以通过创建serializers来实现。serializer与Django的form表单很相似。
我们可以在snippets文件夹下新近啊一个serializer.py的文件,并添加如下代码:

from rest_framework import serializers
from .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):
        """
        :param validated_data: 用于创建Snippet实例的参数
        :return: 类Snippet的实例
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        :param instance: Snippet实例
        :param validated_data: 用于更新Snippet实例的参数
        :return: 更新后的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()方法时候,实例是如何被创建或修改的。

Serializer类和Django的From类很相似,包括对一些用于判断域的参数,比如required, max_length以及default

这些域的参数也可以控制我们的serializer序列化器如何在特定的环境下显示,比如当提供给HTML去渲染时。
参数{'base_template': 'textarea.html'}与Django的Form类中的widget=widgets.Textarea。这在我们的可视化的API中很有用,可以来控制我们的API如何显示。这个我们后面会看一下。
我们也可以直接i使用ModelSerializer类来简化代码。


使用序列化器Serializers

在进一步进行我们的项目之前,我们先来使用熟悉一下Serializer类。
我们进入到Django shell:

python manage.py shell

我们先import一些类,并创建几个实例:

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()

有了这些snippet实例之后,我们现在可以对它们进行序列化。

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

反序列化的过程类似。
首先,我们将数据流解析为Python原生数据类型。

import io

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

然后,我们将这些数据存储到我们的snippet实例中。

serializer = SnippetSerializer(data=data)
serializer.is_valid()
# Trueserializer.validated_data# OrderedDict([('title', ''), ('code', 'print("hello, world!")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object (3)>

通过以上的表现,我们应该可以发现和forms很相似。后面我们在编写视图函数views的时,两者的相似性会更加明显。

除了序列化模型实例之外,我们还可以序列化querysets。序列化querysets的时,我们需要添加一个参数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')])]

使用模型序列化器ModelSerializers

在上面的SnippetSerializer类中,我们从Snippet模型中复制了大量的代码过来。其实,我们可以使这些代码更简明一些。
跟Django中提供了Form和ModelForm两个类相似,REST framework也提供了Serializer类和ModelSerializer类。

我们现在使用ModelSerializer类来重写一下我们的SnippetSerializer类。
打开snippets/serializer.py文件,将SnippetSerializer类修改为:

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

实例serializer有一个很不错的特点功能,我们可以通过内置的repr函数来打印出来实例中的所有域。

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类并没有很特殊的地方,它只不过是用于创建serializer类的一个捷径而已:

  1. 通过集合自动确定域;

  2. 帮我们创建默认的create()update()方法。

使用Serializer写常规的Django视图

现在我们看一下,如果通过我们的Serializer类来写一些API views。
现在,我们先不使用REST framework的一些功能,就先简单写一些常规的Django视图。
编辑snippets/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

首先一个视图,我们需要能列出所有已创建的snippet实例,并能创建新的snippet实例。

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

    if request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        else:
            return JsonResponse(serializer.errors, status=400)

我们现在需要从客户端通过POST提交数据到我们的视图函数,我们需要先给我们的视图函数添加一个csrf_exempt的装饰器。

我们还需要一个视图函数来获取一个snippet实例,对实例进行显示,更新或这删除。

@csrf_exempt
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    :param request:
    :return: HttpResponse or JsonResponse
    """
    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 HttpResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

然后,我们创建snippets/urls.py文件中,写入:

from django.urls import path
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]

tutorial/urls.py文件中include上面的文件:

from django.urls import path, include

urlpatterns = [
    path('', include('snippets.urls')),
]

Web API的测试

我们现在开启我们的web服务。
从shell中退出

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来测试。Httpie是一个基于Python的比较好用的一个http客户端。
我们使用pip安装httpie:

pip install httpie

然后,我们可以通过输入http http://127.0.0.1:8000/snippets/ 命令获取一下所有的snippet:

http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
Content-Length: 356
Content-Type: application/json
Date: Mon, 15 Jul 2019 08:43:44 GMT
Server: WSGIServer/0.2 CPython/3.6.0
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "foo = \"bar\"\n",        
        "id": 1,        
        "language": "python",        
        "linenos": false,        
        "style": "friendly",        
        "title": ""
    },    
    {
        "code": "print(\"hello, world!\")\n",       
        "id": 2,        
        "language": "python",        
        "linenos": false,        
        "style": "friendly",        
        "title": ""
    },    
    {
        "code": "print(\"hello, world!\")",        
        "id": 3,        
        "language": "python",        
        "linenos": false,        
        "style": "friendly",        
        "title": ""
    }
]

或者通过id去获取一个特定的snippet:

http http://127.0.0.1:8000/snippets/2/

HTTP/1.1 200 OK
Content-Length: 121
Content-Type: application/json
Date: Mon, 15 Jul 2019 08:44:07 GMT
Server: WSGIServer/0.2 CPython/3.6.0
X-Frame-Options: SAMEORIGIN

{
    "code": "print(\"hello, world!\")\n",    
    "id": 2,    
    "language": "python",    
    "linenos": false,    
    "style": "friendly",    
    "title": ""
}

我们也可以通过在浏览器中输入这些url地址来查看到这些json数据。

通过以上,我们创建按了序列化API,这一点和Django的Form很类似。然后我还创建了一些常规的Django视图。

在当前,我们的API视图除了提供json数据的展示之外,还没有什么特别的地方。
后面,我们再来继续完善它。


本文中的源码可以在Github上查看。点我查看本文源码


转载请注明:禅思 » Django Rest Framework学习总结-1:序列化(Serialization)?

喜欢 (0) or 分享 (0)

我的个人微信公众号,欢迎关注

扫码或搜索:Python后端开发Django

Python后端开发Django

微信公众号 扫一扫关注

结交朋友、一起学习,一起进步。

科波之主

QQ号 386046154 立即加入

添加微信,进行技术交流

专注技术交流, 一同成长进步

我的微信号

如果您喜欢我的文章,感觉我的文章对您有帮助,请狠狠点击下面

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(26)个小伙伴在吐槽
    1. super active viagra online viagra, no prescription buy viagra online http://genqpviag.com/ - buy viagra generic online ’
      Lbgamomi 2021-01-26 17:33:37 回复
      • cheap viagra photos of real viagra viagra @ walmart http://llviabest.com/ - viagra online mexico ’
        Jdbxamomi 2021-01-29 04:51:36 回复
        • buying viagra without perscription buy generic viagra online overnight viagra street value
          Fvfcaddew 2021-02-05 22:24:27 回复
          • canada pharmacies pharmacy online prescription online
            Kbcxchit 2021-02-07 02:28:45 回复
            • cheap prescription drugs online canada pharmacies online prescriptions hepatitis c virus (hcv)
              Fqhhamomi 2021-02-07 02:49:25 回复
              • order prescriptions online without doctor canadian pharmacy online medicine online shopping
                Jbnbelelm 2021-02-07 10:37:13 回复
                • canadian pharcharmy online bestsellers viagra generic online pharmacy
                  Ahkdamomi 2021-02-07 14:32:04 回复
                  • cialis & viagra suppliers cialas,viagra viagra 50mg
                    KuikDrity 2021-02-07 16:45:45 回复
                    • where to find viagra online viagra action women viagra throught paypall
                      Nncsaddew 2021-02-07 19:10:04 回复
                      • gastro health pharmacy price comparison hormones
                        Jbbvamomi 2021-02-08 16:45:41 回复
                        • legal canadian prescription drugs online viagra .com price pro pharmacy canada
                          Lbsxamomi 2021-02-08 23:45:18 回复
                          • cialis 20mg pills cheapest generic cialis australia how much is a cialis
                            Aqcfsilkdisee 2021-02-09 03:03:21 回复
                            • cialis paypal payment http://llecialisjaw.com/ original cialis
                              Fbsgaddew 2021-02-13 19:18:20 回复
                              • online payday loans collections hard money loans buffalo ny cash star group loans
                                Kvaxchit 2021-02-14 00:33:07 回复
                                • paragon funding payday loans bcs payday loan relief cash loans in rome ga
                                  Jbnvelelm 2021-02-14 08:23:16 回复
                                  • viagra gold overnight nitric oxide and viagra generic viagra
                                    KbbfDrity 2021-02-14 08:36:28 回复
                                    • cash easy loan reviews on payday advance philippines quick cash loans
                                      Fqbbamomi 2021-02-14 09:42:06 回复
                                      • how do you get cash advance on credit card i can't pay back my payday loan cash advance norwich ct
                                        Ahbzamomi 2021-02-15 17:39:58 回复
                                        • cialis efeitos colaterais cialis retseptita cialis and fluoxetine
                                          Jbbnamomi 2021-02-16 11:15:07 回复
                                          • cialis maxman usa confezioni cialis 10 mg tadalafil cialis bula
                                            Labxamomi 2021-02-16 18:39:57 回复
                                            • viagra with priligy http://viagriyvik.com/ viagra soft tabs uk
                                              Abcfsilkdisee 2021-02-17 20:35:05 回复
                                              • Hi Dear, are you in fact visiting this website on a regular basis, if so afterward you will definitely obtain good know-how.
                                                ucuz 2021-02-23 17:41:52 回复
                                                • viagra online usa http://viagraonlinejc.com/ generic viagra over the counter
                                                  Fjjuaddew 2021-02-28 18:06:27 回复
                                                  • what is the shelf life of viagra viagra dose what happens if a girl takes viagra
                                                    Kndnchit 2021-03-01 04:57:19 回复
                                                    • cialis online 2nd day shipping cialis with effexor cialis france
                                                      Nbmoaddew 2021-03-02 14:09:17 回复
                                                      • tadalafil pills 20mg: http://tadalafilonline20.com/ tadalafil 40 mg from india
                                                        MarcosKix 2021-03-05 23:17:13 回复