Frage

Ich habe folgende Modellstruktur:

class Container(models.Model):
    pass

class Generic(models.Model):
    name = models.CharacterField(unique=True)
    cont = models.ManyToManyField(Container, null=True)
    # It is possible to have a Generic object not associated with any container, 
    # thats why null=True

class Specific1(Generic):
    ...

class Specific2(Generic):
    ...

...

class SpecificN(Generic):
    ...

Sag mal, ich brauche alle Specific-Typen Modelle abzurufen, die eine Beziehung mit einem bestimmten Container hat.

Die SQL für das ist mehr oder weniger trivial, aber das ist nicht die Frage. Leider bin ich nicht sehr erfahren mit ORMs (Django ORM insbesondere) bei Arbeiten, so könnte ich hier ein Muster fehlen.

Wenn in einer Brute-Force-Art und Weise getan, -

c = Container.objects.get(name='somename') # this gets me the container
items = c.generic_set.all() 
# this gets me all Generic objects, that are related to the container
# Now what? I need to get to the actual Specific objects, so I need to somehow
# get the type of the underlying Specific object and get it
for item in items:
    spec = getattr(item, item.get_my_specific_type())

Dies führt zu einer Tonne von db-Hits (eine für jede generische Akte, die zu einem Container bezieht), so ist dies offensichtlich nicht der richtige Weg, es zu tun. Nun, es könnte vielleicht durch getan werden, um die SpecificX bekommen Objekte direkt:

s = Specific1.objects.filter(cont__name='somename')
# This gets me all Specific1 objects for the specified container
...
# do it for every Specific type

auf diese Weise die db für jeden Arttypisch einmal getroffen werden (akzeptabel, ich denke).

Ich weiß, dass .select_related () nicht mit m2m Beziehungen nicht funktioniert, so dass es hier nicht viel helfen wird.

Um es zu wiederholen, hat das Endergebnis eine Sammlung von SpecificX Objekten (nicht generisch).

War es hilfreich?

Lösung

Ich glaube, Sie haben bereits die zwei einfache Möglichkeiten skizziert. Entweder Sie machen einen einzigen Filter-Abfrage für Generika und dann werfen Sie jedes Element seinen spezifischen Subtyp (Ergebnisse in n + 1-Abfragen, wobei n die Anzahl der Elemente zurückgegeben), oder Sie machen eine separate Abfrage für jedes spezifische Tabelle (Ergebnisse in k Abfragen, wobei k die Anzahl der spezifischen Typen ist).

Es ist eigentlich wert Benchmarking, welche von diesen ist schneller in der Realität zu sehen. Die zweite scheint besser, weil es (wahrscheinlich) weniger Anfragen, aber jeder dieser Abfragen muss ein mit dem m2m Zwischentisch verbinden zuführen. Im ersteren Fall, dass Sie eine einzige Join-Abfrage, und dann viele einfache. Einige Datenbank-Backends eine bessere Leistung mit vielen kleinen Anfragen als weniger, komplexere.

Wenn die zweiten tatsächlich wesentlich schneller für Ihren Anwendungsfall ist, und Sie sind bereit, einige zusätzliche Arbeit zu tun, um Ihren Code zu reinigen, sollte es möglich sein, eine benutzerdefinierten Manager Methode für das generische Modell zu schreiben, dass „pre-Fetches "all Subtyp Daten aus den entsprechenden spezifischen Tabellen für eine bestimmte queryset, nur eine Abfrage pro Subtyp Tabelle; ähnlich, wie diese Schnipsel generic Fremdschlüssel mit einem Bulk-Prefetch optimiert. Dies würden Sie die gleichen Abfragen als zweite Option, mit der Dryer Syntax Ihrer ersten Option.

Andere Tipps

keine vollständige Antwort, aber Sie können eine große Anzahl von Treffern vermeiden, indem Sie diese

items= list(items)
for item in items:
    spec = getattr(item, item.get_my_specific_type())

statt dies:

for item in items:
    spec = getattr(item, item.get_my_specific_type())

Tatsächlich durch eine Umwandlung in eine Python-Liste zwingen, können Sie die django ORM zwingen, alle Elemente in Ihrem queryset zu laden. Dann tut dies in einer Abfrage.

ich auf die folgenden Posten versehentlich stubmled, die so ziemlich Ihre Frage beantwortet:

http://lazypython.blogspot.com/2008 /11/timeline-view-in-django.html

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top