【问题标题】:Efficient serialization/deserialization of nested models with django rest framework使用 django rest 框架对嵌套模型进行高效的序列化/反序列化
【发布时间】:2017-04-14 06:09:29
【问题描述】:

我有一个可以递归嵌套的 django 模型:

models.py

from django.db import models

class Node(models.Model):
   name = models.CharField(max_length=255)
   parent = models.ForeignKey('Node', related_name='children', null=True)

嵌套模型在我的应用程序中通常有大约 100 个节点。

我正在使用 django rest 框架进行序列化/反序列化:

serializers.py

from rest_framework import serializers
from .models import Node
from django.db import models

class NodeSerializer(serializers.ModelSerializer):

    # Recursive serializers not supported, so use a blank one
    children = serializers.Serializer(many=True, required=False)

    class Meta:
        model = Node
        fields = ('name', 'children')

    def create(self, validated_data):
        # Remove nested objects to handle separately
        children = validated_data.pop('children', [])
        node = Node.objects.create(
            # parent is null only on the root node
            parent=self.context.get('parent'),
            **validated_data)
        for child_data in children:
            s = NodeSerializer(data=child_data
                                context={'parent': node})
            s.is_valid(raise_exception=True)
            s.save()
        return node

    def to_representation(self, instance):
        if not isinstance(instance, models.Model):
            # If the Serializer was instantiated with data instead of a model,
            # "instance" is an OrderedDict.
            return instance
        else:
            # This renders simple fields
            representation = super(NodeSerializer, self)\
                .to_representation(instance)
            # Then recursively render nested models
            representation['children'] = [NodeSerializer(child).data
                                          for child in instance.children.all()]
            return representation

    def validate(self, data):
        # serializers.Serializer couldn't validate children,
        # so we validate here
        children = self.initial_data.get('children')
        if children:
            self._validate_children(children)
            data.update({'children': children})
        return data

    def _validate_children(self, value):
        # TODO
        pass

有了这个,你可以像这样反序列化嵌套模型:

s = NodeSerializer(data={
    'name': 'grandma', 
    'children': [{
        'name': 'dad', 
        'children': [{'name': 'me'}]
        }, {
        'name': 'uncle joe', 
        'children': [{'name': 'cousin frank'}]
        }
    ]})
s.is_valid()
m = s.save()
print m.children.first().children.first().name
# me

你可以这样序列化:

s = NodeSerializer(m)
print s.data
# {'name': u'grandma', 'children': [{'name': u'dad', 'children': [{'name': u'me', 'children': []}]}, {'name': u'uncle joe', 'children': [{'name': u'cousin frank', 'children': []}]}]}

这可行,但序列化和反序列化都会对每个节点进行单独的查询。每次访问数据库大约需要 0.15 秒,对于具有许多节点的模型,请求会超时。

通过执行以下操作之一可以加快序列化速度:

  1. 仅使用立即连接的子 ID 进行渲染,并要求客户端明确请求每个子 ID。对子项使用 prefetch_related 以在单个查询中获取所有子项。
  2. 创建另一个从每个子节点指向根节点的 ForeignKey 字段,然后使用 prefetch_related 一次查找所有嵌套的子节点。

反序列化呢?有没有办法避免对每个节点进行单独的查询?如果没有,还有其他避免请求超时的好策略吗?

【问题讨论】:

  • 就个人而言,我宁愿不(反)序列化嵌套模型,而是明确地完成所有操作。你可能已经看过关于 prefetch_related 的相关文章:ses4j.github.io/2015/11/23/…

标签: django serialization nested django-rest-framework deserialization


【解决方案1】:

您应该覆盖默认视图并自己从数据库中获取数据,以利用数据模型的任何提示。 MPTT算法也可能在这里有所帮助。

还请注意,您不能低于 O(n),因为您需要扫描完整的节点列表以进行序列化/反序列化。

编辑: 我意识到您可以参考数据库查询。如果是这样,MPTT 或 prefetch_related / 自定义数据查询应该会有所帮助

【讨论】:

  • 没错,我有兴趣避免对每个节点进行单独的查询。为清晰起见进行了编辑。
  • MPTT 似乎找到了问题的症结所在。查看实施细节以了解如何使其适用于我的案例。 prefetch_related 有助于序列化,但我认为它对反序列化没有帮助。
猜你喜欢
  • 2014-07-26
  • 2013-05-05
  • 2021-06-23
  • 2017-11-15
  • 1970-01-01
  • 2018-12-13
  • 2016-05-04
  • 2019-12-23
  • 2015-04-12
相关资源
最近更新 更多