【问题标题】:Django QuerySet .defer() problem - bug or feature?Django QuerySet .defer() 问题 - 错误还是功能?
【发布时间】:2011-04-06 18:47:06
【问题描述】:

一个例子胜过千言万语:

   In [3]: User.objects.filter(id=19)[0] == User.objects.filter(id=19)[0]
   Out[3]: True

   In [4]: User.objects.filter(id=19)[0] == User.objects.filter(id=19).defer('email')[0]
   Out[4]: False

它是故意这样工作的吗?

子问题:有什么简单的方法可以从延迟的模型实例中获取常规模型实例?

编辑:

看起来 contenttypes 框架已适当修补: http://code.djangoproject.com/changeset/10523

所以我想说 Model.____eq_____() 运算符不应该看起来像 this

    def __eq__(self, other):
        return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()

但更像这样:

    def __eq__(self, other):
        return ContentType.objects.get_for_model(self) is ContentType.objects.get_for_model(other) and self._get_pk_val() == other._get_pk_val()

这当然是第一次导致两次DB命中,不过好在get_for_model似乎实现了缓存。

【问题讨论】:

  • 以防万一其他人被此绊倒,显然这实际上是一个 Django 错误,应该是 fixed in 1.7

标签: python django orm django-models


【解决方案1】:

延迟查询返回不同的类,由 deferred_class_factory 提供:

# in db/models/query_utils.py

def deferred_class_factory(model, attrs):
    """
    Returns a class object that is a copy of "model" with the specified "attrs"
    being replaced with DeferredAttribute objects. The "pk_value" ties the
    deferred attributes to a particular instance of the model.
    """

它基本上是一个代理,从方法解析顺序可以看出:

>>> x = User.objects.filter(id=1).defer("email")[0]
>>> x.__class__.__mro__
(<class 'django.contrib.auth.models.User_Deferred_email'>, \ 
 <class 'django.contrib.auth.models.User'>, \
 <class 'django.db.models.base.Model'>, <type 'object'>)

【讨论】:

  • 是的,我知道它是一个不同的类,但是模型实例 eq 运算符已经重载(here),所以我希望两个实例指向无论其中一个是否被延迟,相同的 DB 对象都应该是相等的 - 延迟只是一个技术问题。
  • 与此同时,我发现这张票 code.djangoproject.com/changeset/10523 解决了延迟模型和非延迟模型的内容类型不同的问题,这证实了我的观点。
【解决方案2】:

这是正常行为,因为 User.objects.filter(id=19)[0] 将返回一个包含模型所有相关字段的查询集,但 User.objects.filter(id=19).defer( 'email')[0] 将带来一个没有电子邮件的查询集...所以你有两个查询集,一个具有较少的字段。

更新:

测试...

In [30]: a = User.objects.filter(id=1)[0]
In [31]: a
Out[31]: <User: mustafa>

In [27]: b = User.objects.filter(id=1).defer('username')[0]
In [28]: b
Out[28]: <User_Deferred_username: mustafa>

In [32]: a == b
Out[32]: False

In [33]: type(a)
Out[33]: <class 'django.contrib.auth.models.User'>

In [34]: type(b)
Out[34]: <class 'django.contrib.auth.models.User_Deferred_username'>

In [35]: a.username
Out[35]: u'mustafa'

In [36]: b.username
Out[36]: u'mustafa'

Defer Documentation 解释为:

具有延迟字段的查询集仍将返回模型实例。如果您访问该字段(一次一个,而不是一次访问所有延迟字段),将从数据库中检索每个延迟字段。

编辑 2:

In [43]: isinstance(b, a.__class__)
Out[43]: True

In [40]: User.__eq__??
Type:           instancemethod
Base Class:     <type 'instancemethod'>
String Form:    <unbound method User.__eq__>
Namespace:      Interactive
File:           /home/mustafa/python/lib/django/db/models/base.py
Definition:     User.__eq__(self, other)
Source:
def __eq__(self, other):
    return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()

== 是一个简单的比较,它比较两个对象,它没有使用相关的类____eq____方法。

【讨论】:

  • 但索引后我应该在同一个对象上收到不同的views
  • MYYN 解释得更具体,我的回答太容易被误解了......所以我添加了一些例子......
猜你喜欢
  • 2013-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-08
  • 2015-01-20
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多