关于python:Django QuerySet .defer()问题 – bug还是功能?

Django QuerySet .defer() problem - bug or feature?

一个例子胜过千言万语:

1
2
3
4
5
   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

所以我要说的是,这个模型。uuuuuuuuuuuuuuuuuuuuuueq_uuuuuuuuu()操作符不应该像这样:

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

但更像这样:

1
2
    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()

当然,这是第一次导致两个数据库命中,但幸运的是,get-for-u模型似乎实现了缓存。


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

1
2
3
4
5
6
7
8
# 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.
   """

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

1
2
3
4
5
>>> 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'>)


它是正常的行为,因为user.objects.filter(id=19)[0]将返回一个包含模型所有相关字段的查询集,但user.objects.filter(id=19)。defer("email")[0]将带来一个不带电子邮件的查询集…所以您有两个查询集,一个具有更少的字段。

更新:

测试…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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'

延迟文档解释如下:

A queryset that has deferred fields will still return model instances. Each deferred field will be retrieved from the database if you access that field (one at a time, not all the deferred fields at once).

编辑2:

1
2
3
4
5
6
7
8
9
10
11
12
13
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()

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