2009-08-18 6 views
2

Ich habe Setup Django Modelle auf folgende Weise:Django Abfrage für große Anzahl von Beziehungen

Modell A hat eine Eins-zu-viele-Beziehung B

jeden Datensatz in A hat zwischen 3.000 bis 15.000 zu modellieren Datensätze in B

Was ist der beste Weg, um eine Abfrage zu erstellen, die den neuesten (größten PK) Datensatz in B, die zu einem Datensatz in A für jeden Datensatz in A entspricht? Ist das etwas, dass ich anstelle von Django ORM SQL verwenden muss?

Antwort

2

eine Hilfsfunktion erstellen für sicher die ‚top‘ Extrahieren Element aus einem beliebigen Abfrage-Set. Ich nutze das überall in meinen eigenen Django-Apps.

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    # Extracts a single element collection w/ top item 
    result = queryset[0:1] 

    # Return that element or None if there weren't any matches 
    return result[0] if result else None 

Dies verwendet ein bisschen einen Trick mit der slice operator to add a limit clause onto your SQL.

Verwenden Sie diese Funktion nun an einer beliebigen Stelle, an der Sie das oberste Element eines Abfrage-Sets abrufen müssen. In diesem Fall sollten Sie die Top-B-Punkt für einen bestimmten A erhalten, wo die B durch absteigend pk sortiert sind, als solche:

latest = top_or_none(B.objects.filter(a=my_a).order_by('-pk')) 

Es gibt auch die kürzlich hinzugefügte ‚Max‘ Funktion in Django Aggregation, die Ihnen helfen könnten Holen Sie sich die maximale pk, aber ich mag diese Lösung in diesem Fall nicht, da es Komplexität hinzufügt.

P.S. Ich mag es nicht wirklich, für diese Art von Abfrage auf das 'pk'-Feld zu vertrauen, da einige RDBMS nicht garantieren, dass sequentielle pks mit der logischen Erstellungsreihenfolge identisch sind. Wenn ich eine Tabelle habe, von der ich weiß, dass ich sie auf diese Weise abfragen muss, habe ich normalerweise meine eigene "create" -Datetime-Spalte, mit der ich anstelle von pk bestellen kann.

bearbeiten basierend auf Kommentar:

Wenn Sie lieber queryset verwenden würde [0] können Sie die 'top_or_none' Funktion thusly ändern:

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    try: 
     return queryset[0] 
    except IndexError: 
     return None 

ich dies zunächst nicht vorschlagen weil ich den Eindruck hatte, dass queryset [0] die gesamte Ergebnismenge zurückziehen würde, dann nehme ich das 0. Item. Anscheinend fügt Django auch in diesem Szenario eine 'LIMIT 1' hinzu, also ist es eine sichere Alternative zu meiner Slicing-Version.

Edit 2

Natürlich können Sie auch die Vorteile von Djangos verwandten Manager nehmen hier konstruieren und die queryset durch Ihre ‚A‘ Objekt bauen, je nach Wunsch:

latest = top_or_none(my_a.b_set.order_by('-pk')) 
+0

Was ist der Unterschied zwischen Ergebnis = Queryset [0: 1] und Ergebnis = Queryset [0]? – hekevintran

+0

queryset [0: 1] gibt eine leere Liste zurück, wenn keine übereinstimmenden Elemente vorhanden sind, wohingegen queryset [0] einen IndexError auslöst. –

+0

Danke für die Antwort! – hekevintran

0

Ich glaube nicht, Django ORM kann dies tun (aber ich war angenehm überrascht, bevor ...). Wenn es eine vernünftige Anzahl von A-Datensätzen gibt (oder wenn Sie paging), würde ich einfach eine Methode zu A-Modell hinzufügen, die diesen "neuesten" B-Datensatz zurückgeben würde. Wenn Sie eine Menge von A-Datensätzen erhalten wollen, jede mit ihrem eigenen neuesten B, würde ich zu SQL wechseln.

remeber, dass, egal welche Route Sie nehmen, benötigen Sie einen geeigneten Verbund Index auf B Tabelle, vielleicht eine order_by=('a_fk','-id') zum Meta Unterklasse Hinzufügen

Verwandte Themen