How does determining subclass through hasattr trigger a db query in django's orm?

ぐ巨炮叔叔 提交于 2019-12-13 05:54:31

问题


For example, I am using multi-table inheritance for a class Node with sub-classes ConceptNode and DerivedNode. To determine the type of Node I am dealing with and distribute a function call down to the appropriate subclass, I often have to call hasattr like this:

test_node = Node.objects.all()[0]

if hasattr( test_node, "conceptnode"):
    test_node.conceptnode.myFunction()
elif hasattr( test_node, "derivednode"):
    test_node.derivednode.myFunction()
else:
   raise Exception("Not a valid type.")

I've noticed that this results in multiple db queries, which add up to really slow down some functions I've written.

I've tried another approach using try...catch which does not decrease the number of queries.

test_node = Node.objects.all()[0]

try:
    test_node.conceptnode.myFunction()
except ObjectDoesNotExist:
    test_node.derivednode.myFunction()

My main question is: how does django determine what queries to execute here? I don't see how hasattr is getting translated into a db query.

Also, if anyone can suggest a more efficient way to handle this (esp. from a query count perspective), that would be great too!

EDIT: To dump the sqlite queries performed I did the following:

from django.db import connection
from django import db
db.reset_queries()
hasattr(nds[0],'conceptnode')
hasattr(nds[0],'derivednode')
connection.queries

And I got the following results:

{'sql': u'SELECT "nodes_node"."id", "nodes_node"."name", "nodes_node"."description", "nodes_node"."node_tree_id", "nodes_node"."unique_name", "nodes_node"."last_updated_timestamp", "nodes_node"."order", "nodes_node"."data_json", "nodes_node"."data_json_synchronized" FROM "nodes_node" LIMIT 1',  'time': '0.001'}
{'sql': u'SELECT "nodes_node"."id", "nodes_node"."name", "nodes_node"."description", "nodes_node"."node_tree_id", "nodes_node"."unique_name", "nodes_node"."last_updated_timestamp", "nodes_node"."order", "nodes_node"."data_json", "nodes_node"."data_json_synchronized", "nodes_conceptnode"."node_ptr_id", "nodes_conceptnode"."node_parent_id" FROM "nodes_conceptnode" INNER JOIN "nodes_node" ON ("nodes_conceptnode"."node_ptr_id" = "nodes_node"."id") WHERE "nodes_conceptnode"."node_ptr_id" = 1 ',  'time': '0.000'}
{'sql': u'SELECT "nodes_node"."id", "nodes_node"."name", "nodes_node"."description", "nodes_node"."node_tree_id", "nodes_node"."unique_name", "nodes_node"."last_updated_timestamp", "nodes_node"."order", "nodes_node"."data_json", "nodes_node"."data_json_synchronized" FROM "nodes_node" LIMIT 1',  'time': '0.001'}
{'sql': u'SELECT "nodes_node"."id", "nodes_node"."name", "nodes_node"."description", "nodes_node"."node_tree_id", "nodes_node"."unique_name", "nodes_node"."last_updated_timestamp", "nodes_node"."order", "nodes_node"."data_json", "nodes_node"."data_json_synchronized", "nodes_derivednode"."node_ptr_id", "nodes_derivednode"."node_source_id", "nodes_derivednode"."node_target_id" FROM "nodes_derivednode" INNER JOIN "nodes_node" ON ("nodes_derivednode"."node_ptr_id" = "nodes_node"."id") WHERE "nodes_derivednode"."node_ptr_id" = 1 ',  'time': '0.000'}

The first and third of these are fetching the original node object.


回答1:


You have two instances of the simple node lookup (the LIMIT 1 query) because you are accessing a queryset as a list multiple times.

nodes = Node.objects.all()
nodes[0]
nodes[0]

triggers two queries, whereas:

node = Node.objects.all()[0]
node
node

triggers one. This can seem a bit strange at first, but the key is to remember that Node.objects.all() is not evaluated (no queries are made) until you access it.

As for why a single object lookup runs two queries, you are using multi-table inheritance. If you just have this in your model:

class Node(models.Model):
    pass

class ConceptNode(Node):
    pass

Django will create two tables, with ConceptNode rows having a parent Node.

What you're probably looking for is an abstract base class, which will allow you to share methods and properties between multiple classes while just using a single table for each. Just add abstract = True to the parent class meta:

class Node(models.Model):
    class Meta:
        abstract = True

Finally, note that both of these queries should take almost no time, so I wouldn't worry about it too much.



来源:https://stackoverflow.com/questions/11694883/how-does-determining-subclass-through-hasattr-trigger-a-db-query-in-djangos-orm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!