2010-09-01 4 views
5

Ein Beispiel ist besser als tausend Worte:Django QuerySet .defer() Problem - Fehler oder Funktion?

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 

Ist es so absichtlich arbeiten?

Teilfrage: Gibt es eine einfache Möglichkeit, eine reguläre Modellinstanz von der zurückgestellten zu erhalten?

EDIT:

Es ist wie Content aussieht Rahmen entsprechend gepatcht: http://code.djangoproject.com/changeset/10523

so würde ich sagen, dass das Modell ._____ eq _____() Operator nicht wie this aussehen sollte:

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

aber mehr wie folgt:

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

Dies verursacht natürlich zum ersten Mal zwei DB-Treffer, aber glücklicherweise scheint get_for_model Cache zu implementieren.

+0

Antwort aktualisiert ... – FallenAngel

+0

Nur für den Fall, dass irgendjemand anderes davon stolpern sollte, anscheinend war das in der Tat ein Django-Bug, der in [1.7] behoben werden sollte (https://code.djangoproject.com/ticket/24772)) –

Antwort

2

Latente Abfragen geben eine andere Klasse, von der deferred_class_factory bereitgestellt:

# 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. 
    """ 

Es ist im Grunde ein Proxy, wie Sie von der Methode Auflösung sehen können,:

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

Ja, ich weiß, dass es eine andere Klasse ist, aber Modellinstanz __eq__ operator ist bereits überladen ([hier] (http://code.djangoproject.com/browser/django/tags/releases/1.2.1/django/ db/models/base.py # L355)), so würde ich erwarten, dass zwei Instanzen, die auf dasselbe DB-Objekt verweisen, gleich sein sollten, egal ob einer von ihnen zurückgestellt ist oder nicht - das Verschieben ist nur eine technische Angelegenheit. –

+0

In der Zwischenzeit fand ich dieses Ticket http: // code.djangoproject.com/changeset/10523, das ein Problem mit Inhaltsarten behob, die für zurückgestellte und nicht zurückgestellte Modelle unterschiedlich sind, bestätigt meine Sichtweise. –

0

Es ist das normale Verhalten , Da User.objects.filter (id = 19) [0] einen Abfragesatz mit allen verwandten Feldern des Modells zurückgibt, aber User.objects.filter (id = 19) .defer ('email') [0] bringt ein Abfrage-Set ohne E-Mail ... Also haben Sie zwei Abfrage-Sets, eines mit einem weniger Feld.

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 erklärt dies so:

A queryset die latente Felder hat noch Modellinstanzen zurück. Jedes zurückgestellte Feld wird aus der Datenbank abgerufen, wenn Sie auf dieses Feld zugreifen (nacheinander, nicht alle aufgeschobenen Felder gleichzeitig).

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

== ist ein einfacher Vergleich, und es vergleicht zwei Objekte, ist es nicht verwandte Klasse ____eq____ Methode verwendet.

+0

Aber nach der Indizierung sollte ich verschiedene "Ansichten" auf dem gleichen Objekt erhalten. –

+0

Die MYYN erklären es mehr und meine Antwort ist so missverständlich ... Also füge ich ein Beispiel hinzu ... – FallenAngel