在django restframework中实现google的部分响应
djangorestframework-jsonmask的Python项目详细描述
概述
在django restframework中实现google的部分响应
要求
- Python(2.7、3.6、3.7)
- Django(1.11、2.0、2.1)
安装
使用pip
安装…
$ pip install djangorestframework-jsonmask
示例
大多数支持?fields=
样式数据剪枝的drf插件都是在serializaton层进行剪枝的。许多水合物完整的orm对象,包括它们的所有详细关系,然后在json序列化之前立即剪切不需要的数据。任何不需要的相关数据仍然会从数据库中获取并水合到django orm对象中,这严重破坏了字段修剪的有用性。
rest_framework_jsonmask
旨在通过允许开发人员声明性地增加与单个请求直接相关的queryset来做得更好。在此模式下,您只需在viewset.queryset上声明基本的queryset和任何通用关系,而将所有其他增强保留为运行时opt-ins。
使用^ {CD3>},首先在适当的代码中包含它的视图集和序列化混合器。以下示例取自此库自己的单元测试中使用的小型项目。
# api/views.pyfromrest_framework_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.pyfromrest_framework_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", } }]
因为没有提供?fields
querystring参数,所以仍像正常一样加载和序列化作者记录。
Note:
rest_framework_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 viaselected_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
很少使用但也很昂贵的关系呢?rest_framework_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