在django restframework中实现google的部分响应(来自zapier包的fork)

drf-jsonmask的Python项目详细描述


Build StatusCoverage StatusPyPI Version


概述

https://github.com/zapier/django-rest-framework-jsonmask分叉。

在django restframework中实现google的部分响应。

要求

  • Python(3.6,3.7)
  • Django(1.11、2.0、2.1)

安装

使用pip安装…

$ pip install drf-jsonmask

示例

大多数支持?fields=样式数据剪枝的drf插件都是在serializaton层进行剪枝的。许多水合物完整的orm对象,包括它们的所有详细关系,然后在json序列化之前立即剪切不需要的数据。任何不需要的相关数据仍然会从数据库中获取并水合到Django ORM对象中,这严重破坏了字段修剪的有用性

drf_jsonmask旨在通过允许开发人员声明性地增加与单个请求直接相关的queryset来做得更好。在此模式下,您只需在ViewSet.queryset上声明基本的queryset和任何通用关系,而将所有其他增强保留为运行时opt-ins

使用^ {CD3>},首先在适当的代码中包含它的视图集和序列化混合器。以下示例取自此库自己的单元测试中使用的小型项目

# api/views.pyfromdrf_jsonmask.viewsimportOptimizedQuerySetMixinclassTicketViewSet(OptimizedQuerySetMixin,viewsets.ReadOnlyModelViewSet):# Normally, for optimal performance, you would apply the `select_related('author')`# call to the base queryset, but that is no longer desireable for data relationships# that your frontend may stop asking for.queryset=Ticket.objects.all()serializer_class=TicketSerializer# Data-predicate declaration is optional, but encouraged. This# is where the library really shines!@data_predicate('author')defload_author(self,queryset):returnqueryset.select_related('author')# api/serializers.pyfromdrf_jsonmask.serializersimportFieldsListSerializerMixinclassTicketSerializer(FieldsListSerializerMixin,serializers.ModelSerializer):# Aside from the mixin, everything else is exactly like normalauthor=UserSerializer()classMeta:models=my_module.models.Ticketfields=('id','title','body','author',)

现在,您已经将api设置为跳过不必要的连接(可能还有预取),除非请求客户端需要该数据。让我们考虑几个假设的请求和它们各自将收到的响应。(为简洁起见,在这些示例中,我将假设分页已关闭。)

GET /api/tickets/200 OK[    {        "id": 1,        "title": "This is a ticket",        "body": "This is its text",        "author": {            "id": 5,            "username": "HomerSimpson",        }    }]

因为没有提供?fieldsquerystring参数,所以仍像正常一样加载和序列化作者记录。

Note: drf_jsonmask treats all requests that lack any field definition as if all possible data is requested, and thus executes all data predicates. In the above example, author data was loaded via selected_related('author'), and not N+1 queries.


GET /api/tickets/?fields=id,title,body200 OK[    {        "id": 1,        "title": "This is a ticket",        "body": "This is its text"    }]

在这个例子中,由于没有指定author,它不仅没有在响应负载中返回,而且从一开始就没有被查询或序列化过。


GET /api/tickets/?fields=id,title,body,author/username200 OK[    {        "id": 1,        "title": "This is a ticket",        "body": "This is its text",        "author": {            "username": "HomerSimpson",        }    }]

在本例中,author数据是通过?fields声明加载的,但是响应中不会出现不需要的键。

嵌套关系

这一切都很好也很有趣,但是如果author很少使用但也很昂贵的关系呢?drf_jsonmask通过上述完全相同的机制支持这一点,尽管有时对细节的额外关注可能很重要。现在假设AuthorSerializer看起来是这样:

classAuthorSerializer(FieldsListSerializerMixin,serializers.ModelSerializer):accounts=AccountSerializer(many=True)classMeta:model=settings.AUTH_USER_MODELfields=('id','username','email','photo','accounts',...)

当然,如果accounts是敏感的内部数据,那么您可能不使用this序列化程序进行外部api使用。当然,这将解决如何决定是否序列化accounts数据的问题——提供的序列化程序对该字段一无所知但是,假设在我们的例子中,accounts对于公共消费是安全的,并且一些票务api调用需要它来进行测试,而其他的则不需要。在这种情况下,我们将重新定义视图集,如下所示:

classTicketViewSet(OptimizedQuerySetMixin,viewsets.ReadOnlyModelViewSet):queryset=Ticket.objects.all()serializer_class=TicketSerializer@data_predicate('author')defload_author(self,queryset):returnqueryset.select_related('author')# Add this extra data_predicate with prefetches `accounts` if and only if# the requests promises to use that information@data_predicate('author.accounts')defload_author_with_accounts(self,queryset):returnqueryset.select_related('author').prefetch_related('author__accounts')

现在,由客户决定以下哪一个选项(或任何其他可以想象的选项)最合适:

 # Includes specified local fields plus all author fields and relationshipsGET /api/tickets/?fields=id,title,author200 OK[    {        "id": 1,        "title": "This is a ticket",        "author": {            "id": 5,            "username": "HomerSimpson",            "accounts": [                {"all_fields": "will_be_present"}            ]        }    }]

# Includes specified local fields plus specified author fields and relationshipsGET /api/tickets/?fields=id,title,author(username,photo)200 OK[   {       "id": 1,       "title": "This is a ticket",       "author": {           "username": "HomerSimpson",           "photo": "image_url"       }   }]

# Includes specified local fields plus specified author fields and relationships plus specified accounts fields and relationshipsGET /api/tickets/?fields=id,title,author(id,accounts(id,type_of,date))200 OK[   {       "id": 1,       "title": "This is a ticket",       "author": {           "id": 5,           "accounts": [               {                   "id": 8001,                   "type_of": "business",                   "date": "2018-01-01T12:00:00Z"               },               {                   "id": 6500,                   "type_of": "trial",                   "date": "2017-06-01T12:00:00Z"               }           ]       }   }]

简言之,只要序列化程序的整个链实现FieldsListSerializerMixin,就可以实现?fields声明的任意深度嵌套。然而,在实践中,由于关系是昂贵的水合物,您可能希望限制该信息,并使用视图集方法上的@data_predicate装饰器控制实际加载的数据

测试

$ make tests

或者让他们在变化中继续前进:

$ make watch

您还可以使用优秀的tox测试工具对所有受支持的python和django版本运行测试。全局安装tox,然后只需运行:

$ tox

文件

$ make docs

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java Hazelcast CP子系统FencedLock内存使用   java Recaptcha和selenium   基于APR的Apache Tomcat本机库允许在生产环境中实现最佳性能,但在java上找不到。图书馆路径   java循环以相反的顺序打印从2到100的偶数   java JsonPropertyOrder不适用于javax的响应   java在微服务体系结构中,为什么他们说共享REST客户端库不好?   java Androidkeep连接到网站以接收数据库通知   java程序,该程序应该移动一个球,但不执行方法run   java表在更新时不更新,但返回相反的值   java Spring事务,EntityManager生命周期   java为什么我的pbkdf2实现如此缓慢(相对于SQLCipher)?   java在特定模式后提取子字符串   java重复数组列表项   java将base64字符串图像显示为超链接   RMIO Java RMI连接拒绝   运行java项目时出错   java IllegalBlockSizeException:使用填充密码解密时,输入长度必须是8的倍数   在Java中检测MySQL BLOB的mimetype   java我可以合理地期望从系统中得到的最差的解决方案是什么。纳米时代?