用 Django 零成本快速实现 API 服务

2016-06-15 11:30:53 +08:00
 rapospectre

写后台的时候经常需要写数据接口,这时就需要用到序列化工具, 而默认到序列化工具对 Django model 的序列化支持有限。 同时 Django 本身的序列化工具我在使用时也觉得有诸多不便,因此我尝试自己写了一个快速序列化工具,帮助我快速简单的实现数据接口。

Django Simple Serializer 是一个可以帮助开发者快速将 Django 数据或者 python data 序列化为 json|raw 数据的工具。

##为什么要用 Django Simple Serializer ?

对于序列化 Django 数据的解决方案已经有以下几种:

###django.core.serializers Django 内建序列化器, 它可以序列化 Django model query set 但无法直接序列化单独的 Django model 数据。如果你的 model 里含有混合数据 , 这个序列化器同样无法使用(如果你想直接使用序列化数据). 除此之外, 如果你想直接把序列化数据返回给用户,显然它包含了很多敏感及对用户无用对信息。 ###QuerySet.values() 和上面一样, 如果你的 model 里有 DateTimeField 或者其他特殊的 Field 以及额外数据, QuerySet.values() 同样没法工作。 ###django-rest-framework serializers django-rest-framework 是一个可以帮助你快速构建 REST API 的强力框架。 他拥有完善的序列化器,但在使用之前你需要花费一些时间入门, 并学习 cbv 的开发方式, 对于有时间需求的项目或者已经存在的项目需要增加数据接口时显然这不是最好的解决方案。 ###django simple serializer 我希望可以快速简单的序列化数据, 所以我设计了一种可以不用任何额外的配置与学习而将 Django data 或者 python data 序列化为相应的数据的简单的方式。 这就是为什么我写了 django simple serializer 。


##运行需求 Django >= 1.5

Python 2.5 及以上 (暂不支持 python 3)

##安装

Install using pip:

pip install django-simple-serializer

##使用 django simple serializer 进行开发 ###序列化 Django data 假设我们有以下 Django models :

class Classification(models.Model):
    c_name = models.CharField(max_length=30, unique=True)

class Article(models.Model):
    caption = models.CharField(max_length=50)
    classification = models.ForeignKey(Classification, related_name='cls_art')
    content = models.TextField()
    publish = models.BooleanField(default=False)

使用 django simple serializer 的简单例子:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list)

data:

[{'read_count': 0, 'create_time': 1432392456.0, 'modify_time': 1432392456.0, 'sub_caption': u'first', 'comment_count': 0, u'id': 31}, {'read_count': 0, 'create_time': 1432392499.0, 'modify_time': 1432392499.0, 'sub_caption': u'second', 'comment_count': 0, u'id': 32}]

默认情况下, 序列器会返回一个 list 或者 dict(对于单个 model 实例), 你可以设置参数 “ output_type ” 来决定序列器返回 json/raw.


##API 手册

####dss.Serializer 提供序列器

function serializer(data, datetime_format='timestamp', output_type='raw', include_attr=None, except_attr=None, foreign=False, many=False)

####Parameters:

####用法:

datetime_format:

|parameters|intro| | -------------- | :---: | |string|转换 datetime 为字符串。如: "2015-05-10 10:19:22"| |timestamp|转换 datetime 为时间戳。如: "1432124420.0"|

例子:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list, datetime_format='string', output_type='json')

data:

[
    {
        "read_count": 0,
        "sub_caption": "first",
        "publish": true,
        "content": "first article",
        "caption": "first",
        "comment_count": 0,
        "create_time": "2015-05-23 22:47:36",
        "modify_time": "2015-05-23 22:47:36",
        "id": 31
    },
    {
        "read_count": 0,
        "sub_caption": "second",
        "publish": false,
        "content": "second article",
        "caption": "second",
        "comment_count": 0,
        "create_time": "2015-05-23 22:48:19",
        "modify_time": "2015-05-23 22:48:19",
        "id": 32
    }
]

output_type

|parameters|intro| | -------------- | :---: | |raw|将 list 或 dict 中的特殊对象序列化后输出为 list 或 dict| |dict|同 raw|
|json|转换数据为 json|

~~xml 转换数据为 xml~~ (暂时去除)

例子:

from dss.Serializer import serializer
article_list = Article.objects.all()[0]
data = serializer(article_list, output_type='json')

data:

{
        "read_count": 0,
        "sub_caption": "first",
        "publish": true,
        "content": "first article",
        "caption": "first",
        "comment_count": 0,
        "create_time": "2015-05-23 22:47:36",
        "modify_time": "2015-05-23 22:47:36",
        "id": 31
    }

include_attr

例子:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list, output_type='json', include_attr=('content', 'caption',))

data:

[
    {
        "content": "first article",
        "caption": "first"
    },
    {
        "content": "second article",
        "caption": "second"
    }
]

exclude_attr

例子:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list, output_type='json', except_attr=('content',))

data:

    [
        {
            "read_count": 0,
            "sub_caption": "first",
            "publish": true,
            "caption": "first",
            "comment_count": 0,
            "create_time": 1432392456,
            "modify_time": 1432392456,
            "id": 31
        },
        {
            "read_count": 0,
            "sub_caption": "second",
            "publish": false,
            "caption": "second",
            "comment_count": 0,
            "create_time": 1432392499,
            "modify_time": 1432392499,
            "id": 32
        }
    ]

foreign

序列化数据中的 ForeignKeyField 及其子项目

例子:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list, output_type='json', include_attr=('classification', 'caption', 'create_time', foreign=True)

data:

    [
        {
            "caption": "first",
            "create_time": 1432392456,
            "classification": {
                "create_time": 1429708506,
                "c_name": "python",
                "id": 1,
                "modify_time": 1429708506
            }
        },
        {
            "caption": "second",
            "create_time": 1432392499,
            "classification": {
                "create_time": 1430045890,
                "c_name": "test",
                "id": 5,
                "modify_time": 1430045890
            }
        }
    ]

many 序列化 ManyToManyField

example:

from dss.Serializer import serializer
article_list = Article.objects.all()
data = serializer(article_list, output_type='json', include_attr=('classification', 'caption', 'create_time', many=True)

测试数据无 ManyToManyField ,数据格式同上

####dss.Mixin 提供序列器 Mixin

class JsonResponseMixin(object)
    datetime_type = 'string'                # 输出 datetime 时间格式。默认为“ string ”,可选参数相见 dss.Serializer.serializer
    foreign = False                         # 是否序列化 ForeignField 。默认为 False
    many = False                            # 是否序列化 ManyToManyField 。默认为 False
    include_attr = None                     # 只序列化 include_attr 包含的属性。默认为 None,接受一个包含属性名称的 tuple
    exclude_attr = None                     # 不序列化 exclude_attr 包含的属性。默认为 None,接受一个包含属性名称的 tuple

####说明:

将普通 class based view 转换为返回 json 数据的 class based view ,适用于 DetailView 等

####用法:

例子:

# view.py
from dss.Mixin import JsonResponseMixin
from django.views.generic import DetailView
from model import Article

class TestView(JsonResponseMixin, DetailView):
    model = Article
    datetime_type = 'string'
    pk_url_kwarg = 'id'


# urls.py
from view import TestView
urlpatterns = patterns('',
    url(r'^test/(?P<id>(\d)+)/$', TestView.as_view()),
)

访问:localhost:8000/test/1/

response:

{
    "article": {
        "classification_id": 5, 
        "read_count": 0, 
        "sub_caption": "second", 
        "comments": [], 
        "content": "asdfasdfasdf", 
        "caption": "second", 
        "comment_count": 0, 
        "id": 32, 
        "publish": false
    }, 
    "object": {
        "classification_id": 5, 
        "read_count": 0, 
        "sub_caption": "second", 
        "comments": [], 
        "content": "asdfasdfasdf", 
        "caption": "second", 
        "comment_count": 0, 
        "id": 32, 
        "publish": false
    }, 
    "view": ""
}

class MultipleJsonResponseMixin(JsonResponseMixin):

####说明:

将列表类视图转换为返回 json 数据的类视图,适用于 ListView 等

####用法:

例子:

# view.py
from dss.Mixin import MultipleJsonResponseMixin
from django.views.generic import ListView
from model import Article

class TestView(MultipleJsonResponseMixin, ListView):
    model = Article
    query_set = Article.objects.all()
    paginate_by = 1
    datetime_type = 'string'


# urls.py
from view import TestView
urlpatterns = patterns('',
    url(r'^test/$', TestView.as_view()),
)

访问:localhost:8000/test/

response:

{
    "paginator": "", 
    "article_list": [
        {
            "classification_id": 1, 
            "read_count": 2, 
            "sub_caption": "first", 
            "content": "first article", 
            "caption": "first", 
            "comment_count": 0, 
            "publish": false, 
            "id": 31
        }, 
        {
            "classification_id": 5, 
            "read_count": 0, 
            "sub_caption": "", 
            "content": "testseteset", 
            "caption": "hehe", 
            "comment_count": 0, 
            "publish": false, 
            "id": 33
        }, 
        {
            "classification_id": 5, 
            "read_count": 0, 
            "sub_caption": "second", 
            "content": "asdfasdfasdf", 
            "caption": "second", 
            "comment_count": 0, 
            "publish": false, 
            "id": 32
        }
    ], 
    "object_list": [
        {
            "classification_id": 1, 
            "read_count": 2, 
            "sub_caption": "first", 
            "content": "first article", 
            "caption": "first", 
            "comment_count": 0, 
            "publish": false, 
            "id": 31
        }, 
        {
            "classification_id": 5, 
            "read_count": 0, 
            "sub_caption": "", 
            "content": "testseteset", 
            "caption": "hehe", 
            "comment_count": 0, 
            "publish": false, 
            "id": 33
        }, 
        {
            "classification_id": 5, 
            "read_count": 0, 
            "sub_caption": "second", 
            "content": "asdfasdfasdf", 
            "caption": "second", 
            "comment_count": 0, 
            "publish": false, 
            "id": 32
        }
    ], 
    "page_obj": {
        "current": 1, 
        "next": 2, 
        "total": 3, 
        "page_range": [
            {
                "page": 1
            }, 
            {
                "page": 2
            }, 
            {
                "page": 3
            }
        ], 
        "previous": null
    }, 
    "is_paginated": true, 
    "view": ""
}

class FormJsonResponseMixin(JsonResponseMixin):

####说明:

将普通 class based view 转换为返回 json 数据的 class based view ,适用于 CreateView 、 UpdateView 、 FormView 等

####用法:

例子:

# view.py
from dss.Mixin import FormJsonResponseMixin
from django.views.generic import UpdateView
from model import Article

class TestView(FormJsonResponseMixin, UpdateView):
    model = Article
    datetime_type = 'string'
    pk_url_kwarg = 'id'


# urls.py
from view import TestView
urlpatterns = patterns('',
    url(r'^test/(?P<id>(\d)+)/$', TestView.as_view()),
)

访问:localhost:8000/test/1/

response:

{
    "article": {
        "classification_id": 5, 
        "read_count": 0, 
        "sub_caption": "second", 
        "content": "asdfasdfasdf", 
        "caption": "second", 
        "comment_count": 0, 
        "id": 32, 
        "publish": false
    }, 
    "form": [
        {
            "field": "caption"
        }, 
        {
            "field": "sub_caption"
        }, 
        {
            "field": "read_count"
        }, 
        {
            "field": "comment_count"
        }, 
        {
            "field": "classification"
        }, 
        {
            "field": "content"
        }, 
        {
            "field": "publish"
        }
    ], 
    "object": {
        "classification_id": 5, 
        "read_count": 0, 
        "sub_caption": "second", 
        "content": "asdfasdfasdf", 
        "caption": "second", 
        "comment_count": 0, 
        "id": 32, 
        "publish": false
    }, 
    "view": ""
}

####对额外数据的序列化支持:

当我们想在 model 中加入一些额外的数据并也想被序列化时, 现在可以这样做:

    def add_extra(article):
        comments = Comment.objects.filter(article=article)
        setattr(article, 'comments', comments)
    
    articles = Article.objects.all()
    map(add_extra, articles)
    result = serializer(articles)

序列化的结果数据中将会包含"comments"哦.

额外加入的数据可以是一个普通的数据类型、 另一个 Django model 、 字典、 列表甚至 QuerySet

django simple serializer 的实际例子: 个人网站后台数据接口

项目地址: django-simple-serializer

欢迎大家拍砖并提供宝贵意见。

8469 次点击
所在节点    Python
11 条回复
felixzhu
2016-06-15 11:58:45 +08:00
这个和 django-rest-framework 提供的 serializer 之类的有什么区别啊
rapospectre
2016-06-15 12:06:46 +08:00
@felixzhu 序列化的结果是一样的,但是我写的这个使用非常简单, 直接用就可以。 django-rest-framework 和它相比的弊端我在文中提到了。当然已经使用 drf 并且非常顺手也可以看看我这个呀,吐吐槽,看看哪里做的不好 0.0
brucedone
2016-06-15 12:54:08 +08:00
why not flask ?
rapospectre
2016-06-15 13:51:23 +08:00
@brucedone 这就是给 Django 的一个解决方案,当然用 flask 做 API 服务也可以,@-@
daimoon
2016-06-16 08:13:33 +08:00
不过我还是觉的有必要花时间学习下 restframework 。学习成本略高,确实非常方便。
rapospectre
2016-06-16 09:42:24 +08:00
@daimoon 是的,不过你可以拿出学 drf 百分之一的时间试试这个呀,也很方便哒。
nimdanoob
2016-06-16 15:41:48 +08:00
good work
rapospectre
2016-06-23 09:52:25 +08:00
@nimdanoob 谢谢!
KgM4gLtF0shViDH3
2017-05-21 18:50:03 +08:00
看 GitHub 上已经支持 python3 了,作者好勤快呀,支持一个。
rapospectre
2017-05-22 16:15:45 +08:00
@bestkayle 谢谢支持!
KgM4gLtF0shViDH3
2017-05-22 18:54:03 +08:00
@rapospectre #10 还需要头条大神的指教呢-0-

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/285856

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX