Djangoクエリセットに基づいてJSONを作成するPython(Django)の最速の方法は何ですか。ここで提案されているようにテンプレートで解析することはオプションではないことに注意してください。
背景には、ツリー内のすべてのノードをループするメソッドを作成しましたが、約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:解決策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(pre_fetchにselect_relatedを使用しますが、正しく使用されているかどうかはわかりません)
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を使用して作成されたプロファイリング結果を示します。これは、スタンドアロンメソッドprofileJSON()を開始するDjangoビューを設定し、JSON出力を作成するためにさまざまなソリューションを呼び出します。
def startProfileJSON(request):
print "startProfileJSON"
import cProfile
cProfile.runctx('profileJSON()', globals=globals(), locals=locals())
print "endProfileJSON"
結果:
解決策1: 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): 3.663秒で2593122関数呼び出し(2299165プリミティブ呼び出し)(詳細)
討論:
解決策1:独自のエンコーディング実装。悪いアイデア
解決策2+3:現在は最速ですが、それでも痛々しいほど遅い
解決策4:子をキャッシュすることで有望に見えますが、子がdouble []に入れられるため、同様のパフォーマンスを示し、現在は無効なjsonを生成します。
"children": [[]] instead of "children": []
解決策5:select_relatedを使用しても違いはありませんが、ノードには常に親への外部キーがあり、ルートから子へと解析しているため、おそらく間違った方法で使用されます。
更新:解決策6:子ノードのキャッシュを使用した、私にとって最もクリーンな解決策のように見えます。しかし、ソリューション2+3と同様にしか機能しません。これは私にとっては奇妙なことです。
パフォーマンスを改善するためのアイデアは他にありますか?