Django-从缓存查询填充模型实例相关字段
情况与问题相同,但问题不同: 我有一个模型Django-从缓存查询填充模型实例相关字段,django,django-orm,Django,Django Orm,情况与问题相同,但问题不同: 我有一个模型节点,看起来像这样: class Node(models.Model): parent = models.ForeignKey('self', related_name='children', on_delete=models.CASCADE, null=True) def cache_children(node): for child in node.children.all(): cache_children(chi
节点
,看起来像这样:
class Node(models.Model):
parent = models.ForeignKey('self', related_name='children', on_delete=models.CASCADE, null=True)
def cache_children(node):
for child in node.children.all():
cache_children(child)
root_node = Node.objects.prefetch_related('children').get(pk=my_node_id)
all_nodes = Node.objects.all() # get all the nodes in a single query
# Currently: hit database for every loop
# Would like: to somehow use the already loaded data from all_nodes
cache_children(root_node)
一个节点可以有几个子节点,每个子节点都可以有自己的子节点
我想这样做:
class Node(models.Model):
parent = models.ForeignKey('self', related_name='children', on_delete=models.CASCADE, null=True)
def cache_children(node):
for child in node.children.all():
cache_children(child)
root_node = Node.objects.prefetch_related('children').get(pk=my_node_id)
all_nodes = Node.objects.all() # get all the nodes in a single query
# Currently: hit database for every loop
# Would like: to somehow use the already loaded data from all_nodes
cache_children(root_node)
由于我已经抓取了all_nodes
查询中的所有节点,因此我希望重用此查询中的缓存数据,而不是每次执行一个新的查询
有什么方法可以做到这一点吗?树状结构中的数据并不非常适合关系数据库,但是有一些策略可以解决这一问题-请参阅第章 如果您的树不是太大,您可以将树完全存储在python dict中并缓存结果 示例(未经测试-根据您的喜好调整数据结构…:
from django.core.cache import cache
# ...
def get_children(nodes, node):
node['children'] = [n for n in nodes if n['parent']==node['id']]
for child_node in node['children']:
child_node = get_children(nodes, child_node)
return node
def get_tree(timeout_in_seconds=3600)
tree = cache.get('your_cache_key')
if not tree:
# this creates a list of dicts with the instances values - one DB hit!
all_nodes = list(Node.objects.all().values())
root_node = [n for n in nodes if n['parent']==None][0]
tree = get_children(all_nodes, root_node)
cache.set('your_cache_key', tree, timeout_in_seconds)
return tree
- 当然,你必须有你的
- 您可以使Node.save方法中的缓存无效
def populate_prefetch_cache(node, all_nodes):
children = [child for child in all_nodes if child.parent_id==node.id]
# will not have the attribute if no prefetch has been done
if not hasattr(node, '_prefetched_objects_cache'):
node._prefetched_objects_cache = {}
# Key to using local data to populate a prefetch!
node._prefetched_objects_cache['children'] = children
node._prefetch_done = True
for child in node.children.all():
populate_prefetch_cache(child , all_nodes )
all_nodes = list(Node.objects.all()) # Hit database once
root_node = Node.objects.get(pk=my_node_id) # Hit database once
# Does not hit the database and properly populates the children field
populate_prefetch_cache(root_node, all_nodes)
由于这个答案,我发现了
\u prefetched\u objects\u cache
属性:实际上,我对“构建您的树”部分非常感兴趣:D。在我看来,我需要过滤我的所有节点
查询,以找到每个节点的子节点并构建树,但这将导致每个过滤器都有一个新的数据库查询…当出现错误时,您将在内存中获得结果,而不会再出现任何数据库命中。顺便说一句:您的parent
字段应该允许null,因为它对于根节点的值是多少?不幸的是,每次我使用filter()
查询都会重新计算。即使我在已经计算过一次的所有节点上执行此操作。关于null=True
,您是对的-修复它您不会使用过滤器,而是用常规Python处理数据。我已经用一个简单的例子更新了我的答案。好的,谢谢你的指点!不幸的是,我不能按照你的方式分配孩子。获取“节点”对象不支持项分配
。不过我还是设法让它工作了(见我的答案)