现在试图解决这个问题几个小时而且没有任何进展。
class other(models.Model):
user = models.ForeignKey(User)
others = other.objects.all()
o = others[0]
此时ORM没有要求o.user对象,但是如果我做任何触及该对象的东西,它会从数据库中加载它。
type(o.user)
将导致数据库加载。
我想要了解的是他们如何做到这一点。什么是导致它发生的pythonic精灵尘埃。是的,我看过来源,我很难过。
答案 0 :(得分:52)
Django使用metaclass(django.db.models.base.ModelBase
)来自定义模型类的创建。对于在模型上定义为类属性的每个对象(user
是我们关注的那个),Django首先查看它是否定义了contribute_to_class
方法。如果定义了方法,Django会调用它,允许对象在创建模型类时自定义它。如果对象未定义contribute_to_class
,则只需使用setattr
将其分配给该类。
由于ForeignKey
是Django模型字段,因此它定义contribute_to_class
。当ModelBase
元类调用ForeignKey.contribute_to_class
时,分配给ModelClass.user
的值是django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor
的实例。
ReverseSingleRelatedObjectDescriptor
是一个实现Python descriptor protocol的对象,用于自定义当一个实例作为另一个类的属性进行访问时会发生什么。在这种情况下,描述符用于lazily load并在第一次访问时从数据库返回相关的模型实例。
# make a user and an instance of our model
>>> user = User(username="example")
>>> my_instance = MyModel(user=user)
# user is a ReverseSingleRelatedObjectDescriptor
>>> MyModel.user
<django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor object>
# user hasn't been loaded, yet
>>> my_instance._user_cache
AttributeError: 'MyModel' object has no attribute '_user_cache'
# ReverseSingleRelatedObjectDescriptor.__get__ loads the user
>>> my_instance.user
<User: example>
# now the user is cached and won't be looked up again
>>> my_instance._user_cache
<User: example>
每次在模型实例上访问ReverseSingleRelatedObjectDescriptor.__get__
属性时都会调用user
方法,但它足够聪明,只能查找一次相关对象,然后在后续调用中返回缓存版本。
答案 1 :(得分:1)
这不会解释 Django究竟如何,但你所看到的是Lazy Loading的实际应用。延迟加载是一种众所周知的设计模式,可以延迟对象的初始化,直到需要它们为止。在您的情况下,直到o = others[0]
或type(o.user)
执行。这个Wikipedia article可以为您提供有关流程的一些见解。
答案 2 :(得分:0)
属性可用于实现此行为。基本上,您的类定义将生成类似于以下的类:
class other(models.Model):
def _get_user(self):
## o.users being accessed
return User.objects.get(other_id=self.id)
def _set_user(self, v):
## ...
user = property(_get_user, _set_user)
在访问“其他”实例的.user之前,不会执行用户查询。