Python (Django) 中基于 Django 查询集创建 JSON 的最快方法是什么。请注意,在此处建议的模板中对其进行解析不是一种选择。
背景是我创建了一个循环遍历树中所有节点的方法,但是在转换大约 300 个节点时已经非常慢了。我想到的第一个(可能也是最糟糕的)想法是以某种方式“手动”创建 json。请参阅下面的代码。
#! Solution 1 !!#
def quoteStr(input):
return "\"" + smart_str(smart_unicode(input)) + "\""
def createJSONTreeDump(user, node, root=False, lastChild=False):
q = "\""
#open tag for object
json = str("\n" + indent + "{" +
quoteStr("name") + ": " + quoteStr(node.name) + ",\n" +
quoteStr("id") + ": " + quoteStr(node.pk) + ",\n" +
)
childrenTag = "children"
children = node.get_children()
if children.count() > 0 :
#create children array opening tag
json += str(indent + quoteStr(childrenTag) + ": [")
#for child in children:
for idx, child in enumerate(children):
if (idx + 1) == children.count():
//recursive call
json += createJSONTreeDump(user, child, False, True, layout)
else:
//recursive call
json += createJSONTreeDump(user, child, False, False, layout)
#add children closing tag
json += "]\n"
#closing tag for object
if lastChild == False:
#more children following, add ","
json += indent + "},\n"
else:
#last child, do not add ","
json += indent + "}\n"
return json
要渲染的树结构是使用mptt构建的树,其中调用 .get_children() 返回节点的所有子节点。
模型看起来就这么简单,mptt 负责其他一切。
class Node(MPTTModel, ExtraManager):
"""
Representation of a single node
"""
name = models.CharField(max_length=200)
parent = TreeForeignKey('self', null=True, blank=True, related_name='%(app_label)s_%(class)s_children')
在模板中这样创建的预期 JSON结果var root = {{ jsonTree|safe }}
编辑:基于这个答案,我创建了以下代码(绝对是更好的代码),但感觉只是稍微快一点。
解决方案2:
def serializable_object(node):
"Recurse into tree to build a serializable object"
obj = {'name': node.name, 'id': node.pk, 'children': []}
for child in node.get_children():
obj['children'].append(serializable_object(child))
return obj
import json
jsonTree = json.dumps(serializable_object(nodeInstance))
解决方案3:
def serializable_object_List_Comprehension(node):
"Recurse into tree to build a serializable object"
obj = {
'name': node.name,
'id': node.pk,
'children': [serializable_object(ch) for ch in node.get_children()]
}
return obj
解决方案4:
def recursive_node_to_dict(node):
result = {
'name': node.name, 'id': node.pk
}
children = [recursive_node_to_dict(c) for c in node.get_children()],
if children is not None:
result['children'] = children
return result
from mptt.templatetags.mptt_tags import cache_tree_children
root_nodes = cache_tree_children(root.get_descendants())
dicts = []
for n in root_nodes:
dicts.append(recursive_node_to_dict(root_nodes[0]))
jsonTree = json.dumps(dicts, indent=4)
解决方案5(使用select_related到pre_fetch,但不确定是否正确使用)
def serializable_object_select_related(node):
"Recurse into tree to build a serializable object, make use of select_related"
obj = {'name': node.get_wbs_code(), 'wbsCode': node.get_wbs_code(), 'id': node.pk, 'level': node.level, 'position': node.position, 'children': []}
for child in node.get_children().select_related():
obj['children'].append(serializable_object(child))
return obj
解决方案 6(改进的解决方案 4,使用子节点的缓存):
def recursive_node_to_dict(node):
return {
'name': node.name, 'id': node.pk,
# Notice the use of node._cached_children instead of node.get_children()
'children' : [recursive_node_to_dict(c) for c in node._cached_children]
}
通过以下方式调用:
from mptt.templatetags.mptt_tags import cache_tree_children
subTrees = cache_tree_children(root.get_descendants(include_self=True))
subTreeDicts = []
for subTree in subTrees:
subTree = recursive_node_to_dict(subTree)
subTreeDicts.append(subTree)
jsonTree = json.dumps(subTreeDicts, indent=4)
#optional clean up, remove the [ ] at the beginning and the end, its needed for D3.js
jsonTree = jsonTree[1:len(jsonTree)]
jsonTree = jsonTree[:len(jsonTree)-1]
您可以在下面看到分析结果,按照 MuMind 的建议使用 cProfile 创建,设置 Django 视图以启动独立方法 profileJSON(),该方法又调用不同的解决方案来创建 JSON 输出。
def startProfileJSON(request):
print "startProfileJSON"
import cProfile
cProfile.runctx('profileJSON()', globals=globals(), locals=locals())
print "endProfileJSON"
结果:
方案一: 4.969秒内3350347次函数调用(3130372次原语调用)(详情)
解决方案 2: 3.630 秒内 2533705 次函数调用(2354516 次原始调用)(详情)
方案3: 3.684秒内2533621次函数调用(2354441次原语调用)(详情)
解决方案 4: 3.840 秒内 2812725 次函数调用(2466028 次原语调用)(详情)
解决方案 5: 3.779 秒内 2536504 次函数调用(2357256 次原语调用)(详情)
解决方案 6(改进的解决方案 4): 2593122 个函数调用(2299165 个原始调用)在 3.663 秒内(详情)
讨论:
解决方案1:自己的编码实现。馊主意
解决方案 2 + 3:目前最快,但仍然非常缓慢
解决方案 4:缓存孩子看起来很有希望,但确实执行相似并且当前产生无效的 json,因为孩子被放入双 []:
"children": [[]] instead of "children": []
解决方案 5:使用 select_related 并没有什么不同,但可能以错误的方式使用,因为一个节点总是有一个 ForeignKey 到它的父节点,我们正在从根解析到子节点。
更新:解决方案 6:对我来说,它看起来是最干净的解决方案,使用子节点缓存。但只执行类似于解决方案 2 + 3。这对我来说很奇怪。
还有更多关于性能改进的想法吗?