2010-09-01 5 views
5

Un exemple vaut mieux que mille mots:Django QuerySet .defer() problème - bug ou fonctionnalité?

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 

-t-il fonctionner comme ça sur le but?

Sous-question: existe-t-il un moyen simple d'obtenir une instance de modèle régulière à partir de celle différée?

EDIT:

Il ressemble cadre contenttypes est patché appropriée: http://code.djangoproject.com/changeset/10523

Je dirais donc que le modèle ._____ équivalent opérateur _____() ne doit pas ressembler à this:

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

mais plus comme ceci:

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

Cela provoque bien sûr deux hits DB pour la première fois, mais heureusement, get_for_model semble implémenter le cache.

+0

Réponse mise à jour ... – FallenAngel

+0

Juste au cas où quelqu'un d'autre se fait trébucher par cela, apparemment c'était en fait un bug Django qui devrait être [fixé dans 1.7] (https://code.djangoproject.com/ticket/24772 –

Répondre

2

requêtes différées renvoient une autre classe, fourni par le 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. 
    """ 

Il est essentiellement un proxy, comme vous pouvez le voir dans la résolution de la méthode de commande:

>>> 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'>) 
+0

Oui, je sais que c'est une classe différente, mais l'opérateur de modèle __eq__ est déjà surchargé ([ici] (http://code.djangoproject.com/browser/django/tags/releases/1.2.1/django/ db/models/base.py # L355)), donc je m'attendrais à ce que deux instances pointant vers le même objet DB soient égales, peu importe si l'une d'entre elles est différée ou non - différer est juste une question technique. –

+0

Pendant ce temps, j'ai trouvé ce billet http: // code.djangoproject.com/changeset/10523 qui fixe un problème avec contentypes étant différents différés et de modèles non différés, cela confirme mon point de vue. –

0

Son la Behaviour normale , Car User.objects.filter (id = 19) [0] retournera un jeu de requête avec tous les champs liés du modèle, mais User.objects.filter (id = 19) .defer ('email') [0] apportera un queryset sans email ... Donc vous avez deux querysets, un avec moins de champs.

Mise à jour:

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 explique ce que:

Un queryset qui a des champs différés renverrait encore des instances de modèle. Chaque champ différé sera récupéré de la base de données si vous accédez à ce champ (un à la fois, pas tous les champs différés à la fois).

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

== est une comparaison simple et il compare deux objets, il n'est pas en utilisant la méthode de la classe ____eq____ connexes.

+0

Mais après indexation je recevoir différents 'views' sur le même objet –

+0

Le MYYN expliquer plus spesific et ma réponse est si misunderstandable ... J'ajoute donc quelques exemples ... – FallenAngel