2013-10-29 9 views
13

Ich frage mich, ob es einen Weg in Django gibt zu sagen, ob ein verwandtes Feld, insbesondere der "viele" Teil einer Eins-zu-viele-Beziehung, beispielsweise über prefetch_related() geholt wurde, ohne es tatsächlich zu holen?Django: Können Sie feststellen, ob ein zugehöriges Feld vorab abgerufen wurde, ohne es abgerufen zu haben?

So, als Beispiel, lassen Sie uns sagen, ich habe diese Modelle:

class Question(Model): 
    """Class that represents a question.""" 

class Answer(Model): 
    """Class the represents an answer to a question.""" 
    question = ForeignKey('Question', related_name='answers') 

Normalerweise die Anzahl der Antworten auf eine Frage zu erhalten, der effizienteste Weg, dies zu umgehen wäre folgendes zu tun (weil die Django docs sagen, dass count() effizienter ist, wenn man nur eine Zählung) benötigen:

# Note: "question" is an instance of class Question. 
answer_count = question.answers.count() 

jedoch in einigen Fällen sind die Antworten wurden durch über einen prefetch_related() Anruf (oder irgendeine Weise, wie zuvor wiederholt hat geholt können die Antwort s). So in Situationen wie die, wäre es effizienter zu sein, dies zu tun (weil wir die zusätzliche Abfrage der Anzahl überspringen würde):

# Answers were fetched via prefetch_related() 
answer_count = len(question.answers.all()) 

Also, was ich wirklich tun möchte, ist so etwas wie:

if question.answers_have_been_prefetched: # Does this exist? 
    answer_count = len(question.answers.all()) 
else: 
    answer_count = question.answers.count() 

Ich benutze Django 1.4, wenn es darauf ankommt. Danke im Voraus.

Edit: zusätzliche Klarstellung, dass prefetch_related() ist nicht die einzige Möglichkeit, die Antworten abgerufen werden könnten.

+0

Der springende Punkt eine Wrapper-Bibliothek hier verwenden ist, so dass Sie don‘ In der Tat müssen wir uns um solche Dinge sorgen. Wenn Sie dies nicht als echten Engpass gemessen haben, verwenden Sie einfach die einfache Methode und fügen Sie keine unnötige Komplexität hinzu. Allerdings habe ich nach ein wenig Stochern im Django-Quellcode einige Hinweise gefunden. Wenn Sie immer noch darauf bestehen, diesen Hack zu versuchen, können Sie versuchen, obj._prefetched_objects_cache, oder alternativ, versuchen Sie einfach 'print dir (question.answers)' und sehen, ob Sie dort cache-bezogene suchen Parameter dort sehen. – kazagistar

+0

Es ist kein riesiger Flaschenhals, nein. Ich habe nur versucht, die Hölle aus der App wirklich zu optimieren und die Anzahl der Anfragen auf ein Minimum zu reduzieren. Einverstanden, wenn der einzige Weg Hacky ist, dann ist es das nicht wert. Ich sollte klarstellen: prefetch_related war nur ein Beispiel. Die Ergebnisse könnten auch auf andere Art und Weise abgerufen worden sein (z. B. über Frage-Antworten im Code), weshalb ich auf eine allgemeine Lösung gehofft hatte. – Chad

+2

Es wäre wirklich nett, wenn Django Sie für die automatische In-Memory-Filterung von vorab geholten Relationen entscheiden lassen könnte, so dass Sie keine neue Anfrage generieren, indem Sie einfach mit ihrem DSL die Daten untersuchen, die Sie bereits haben. – skatenerd

Antwort

20

Ja, Django speichert die vorab abgerufenen Ergebnisse im _prefetched_objects_cache-Attribut der übergeordneten Modellinstanz.

So können Sie etwas tun:

instance = Parent.objects.prefetch_related('children').all()[0] 

try: 
    instance._prefetched_objects_cache[instance.children.prefetch_cache_name] 
    # Ok, it's pefetched 
    child_count = len(instance.children.all()) 
except (AttributeError, KeyError): 
    # Not prefetched 
    child_count = instance.children.count() 

die relevant use in der django Quelle Stamm Siehe oder the equivalent in v1.4.9

+0

Danke dafür, aber ich sollte klarstellen: prefetch_related war nur ein Beispiel. Die Ergebnisse könnten auch auf andere Weise abgerufen worden sein (z. B. durch Iterieren über fragment.answers zuvor im Code). – Chad

+2

Related Manager-Zugriffe werden normalerweise nicht zwischengespeichert, Sie müssten wahrscheinlich Ihren eigenen Memo-Wrapper implementieren: https://docs.djangoproject.com/en/dev/topics/db/optimization/#understand-queryset-evaluation –

+0

Ah, Ok, das ist gut zu wissen. Sie werden also nur zwischengespeichert, wenn Sie prefetch_related verwenden. Danke für die Klarstellung. Klingt wie dieser Hack ist die Mühe nicht wert und ich sollte nur zählen(), aber Sie gaben mir die Informationen, die ich brauchte. – Chad

Verwandte Themen