【发布时间】: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 秒,对于具有许多节点的模型,请求会超时。
通过执行以下操作之一可以加快序列化速度:
- 仅使用立即连接的子 ID 进行渲染,并要求客户端明确请求每个子 ID。对子项使用 prefetch_related 以在单个查询中获取所有子项。
- 创建另一个从每个子节点指向根节点的 ForeignKey 字段,然后使用 prefetch_related 一次查找所有嵌套的子节点。
反序列化呢?有没有办法避免对每个节点进行单独的查询?如果没有,还有其他避免请求超时的好策略吗?
【问题讨论】:
-
就个人而言,我宁愿不(反)序列化嵌套模型,而是明确地完成所有操作。你可能已经看过关于 prefetch_related 的相关文章:ses4j.github.io/2015/11/23/…。
标签: django serialization nested django-rest-framework deserialization