代码之家  ›  专栏  ›  技术社区  ›  boatcoder

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

  •  23
  • boatcoder  · 技术社区  · 15 年前

    class other(models.Model):
        user = models.ForeignKey(User)
    
    
    others = other.objects.all()
    o = others[0]
    

    此时ORM还没有请求o.user对象,但是如果我做了任何涉及该对象的操作,它就会从数据库中加载它。

    type(o.user)
    

    3 回复  |  直到 15 年前
        1
  •  52
  •   jpwatts    15 年前

    Django使用 metaclass ( django.db.models.base.ModelBase )自定义模型类的创建。对于模型上定义为类属性的每个对象( user contribute_to_class 方法。如果定义了方法,Django将调用它,从而允许对象在创建模型类时自定义模型类。如果对象没有定义 ,只需使用 setattr .

    ForeignKey contribute_to_class . 当 ModelBase 元类调用 ForeignKey.contribute_to_class ,指定给 ModelClass.user 是的实例 django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor .

    ReverseSingleRelatedObjectDescriptor 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__ 每次 用户

        2
  •  1
  •   Manoj Govindan    15 年前

    确切地说,Django是这样做的,但是你看到的是懒惰的加载行为。延迟加载是一种众所周知的设计模式 推迟 对象的初始化一直到需要它们为止。在你的情况下直到 o = others[0] type(o.user) Wikipedia article 也许能让你对这个过程有所了解。

        3
  •  0
  •   Ivo van der Wijk    15 年前

    属性可用于实现此行为。基本上,您的类定义将生成一个类似于以下内容的类:

    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)
    

    在访问“other”实例的.User之前,不会执行对用户的查询。