views:

34

answers:

2

An example is better than a thousand words:

   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

Does it work like this on purpose ?

Subquestion: is there any simple way to get a regular model instance from the deferred one ?

EDIT:

It looks like contenttypes framework is patched appropriately: http://code.djangoproject.com/changeset/10523

so I would say that the Model.__eq__() operator shouldn't look like this:

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

but more like this:

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

This of course causes two DB hits for the first time, but fortunately get_for_model seems to implement cache.

+1  A: 

Deferred queries return a different class, provided by the 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.
    """

It is basically a proxy, as you can see from the method resolution order:

>>> 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'>)
The MYYN
Yes, I know that it is a different class, but model instance __eq__ operator is already overloaded ( [here](http://code.djangoproject.com/browser/django/tags/releases/1.2.1/django/db/models/base.py#L355) ), so I would expect that two instances pointing to the same DB object should be equal no matter if one of them is deferred or not - deferring is just a technical matter.
Tomasz Zielinski
Meantime I found this ticket http://code.djangoproject.com/changeset/10523 which fixed a problem with contentypes being different for deferred and non-deferred models, this confirms my point of view.
Tomasz Zielinski
A: 

Its the normal behaviour, Because User.objects.filter(id=19)[0] will return a queryset with all of the related fields of the model, but User.objects.filter(id=19).defer('email')[0] will bring a queryset without email... So you have two querysets, one with a fewer field.

Update:

Test...

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 explains this as:

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).

EDIT 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()

== is a simple comparison and it compares two objects, it is not using related class _eq_ method.

FallenAngel
But after indexing I should receive different `views` on the same object
Tomasz Zielinski
The MYYN explain it more spesific and my answer is so misunderstandable... So i add some example...
FallenAngel