Django的ORM如何在访问外部对象时设法获取它们

boa*_*der 23 python django orm

一直在努力解决这个问题几个小时而且无处可去.

class other(models.Model):
    user = models.ForeignKey(User)


others = other.objects.all()
o = others[0]
Run Code Online (Sandbox Code Playgroud)

此时ORM没有要求o.user对象,但是如果我做了触及该对象的ANYTHING,它会从数据库中加载它.

type(o.user)
Run Code Online (Sandbox Code Playgroud)

将导致数据库加载.

我想要了解的是他们如何做到这一点.什么是导致它发生的pythonic精灵尘埃.是的,我看过源头,我很难过.

jpw*_*tts 52

Django使用元类(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的描述符协议的对象,以便自定义当一个类的实例作为另一个类的属性被访问时发生的事情.在这种情况下,描述符用于在第一次访问时从数据库延迟加载并返回相关的模型实例.

# 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>
Run Code Online (Sandbox Code Playgroud)

ReverseSingleRelatedObjectDescriptor.__get__每次user在模型实例上访问属性时都会调用该方法,但它足够智能,只能查找一次相关对象,然后在后续调用中返回缓存版本.