Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 439
Tomasz Zieliński Avatar asked Sep 01 '10 12:09

Tomasz Zieliński


People also ask

Why are QuerySets considered lazy?

This is because a Django QuerySet is a lazy object. It contains all of the information it needs to populate itself from the database, but will not actually do so until the information is needed.

What is QuerySet in Django?

A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.

How do I get QuerySet in Django?

You get a QuerySet by using your model's Manager . Each model has at least one Manager , and it's called objects by default. Access it directly via the model class, like so: >>> Blog.objects <django.db.models.manager.Manager object at ...> >>> b = Blog(name='Foo', tagline='Bar') >>> b.objects Traceback: ...


1 Answers

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'>)
like image 151
miku Avatar answered Sep 28 '22 09:09

miku