2015-10-13 17 views
5

Ich vermisse etwas offensichtlich, aber ich habe Probleme, eine Join auf einem ManyToMany Feld in einer Django App arbeiten. Ich habe zwei Modelle:Joining ManyToMany Felder mit prefetch_related in Django

class Area(models.Model): 
    name = CharField(...) 

class Role(models.Model): 
    name = CharField(...) 
    areas = ManyToManyField('Area', ...) 

Mein Ziel ist es, das Äquivalent dieser Abfrage zu haben:

select a.name, r.name 
from area a 
join area_role ar on ar.area_id = a.id 
join role r on ar.role_id = r.id 
order by a.name, r.name 

Der resultierende Datensatz würde wie folgt aussehen:

Area Role 
--------------------- 
A  My Role 
A  Your Role 
A  Yo Mamas Role 
B  My Role 
B  Some Other Role 

Wie Sie sehen in kann Das Beispiel, die Meine Rolle Element erscheint zweimal, einmal für jeden Bereich. Ich weiß, dass ich die Liste der Bereiche bekommen und dann eine Liste von Rollen für jeden bekommen kann (was zu N + 1 Abfragen führt), aber ich möchte, wenn möglich, effizient sein. Als solche habe ich festgestellt, dass prefetch_related könnte was ich verwenden wollte. Wenn ich dies verwende, habe ich jedoch alle Area-Werte als None. Hier ist, was ich versucht habe:

Die Rollennamen kommen für die Fahrt korrekt, aber die Bereichsnamen nicht. Was mache ich hier falsch?

+0

nicht verwandt: warum setzen Sie 'Area' zwischen Anführungszeichen in' Bereiche = ManyToManyField ('Area', ...) '? – Pynchia

+1

'print (" Gebietsname: "+ str (r.areas__name))' meinst du 'print (" Gebietsname: "+ str (r.areas.name))'? – danielcorreia

+1

@Pynchia könnte er Bereich ohne Anführungszeichen in diesem Fall verwenden, da Bereich vor Rollenmodell definiert ist. Wenn Area nach Role definiert wäre, wären Zitate erforderlich. [Vollständige Erklärung hier] (https://docs.djangoproject.com/en/1.8/ref/models/fields/#lazy-relationships) – danielcorreia

Antwort

5

Es ist nicht möglich, auf r.areas__name für eine Rolle r zuzugreifen. Sie müssen weiterhin über r.areas.all() auf die Rollen zugreifen. Wenn Sie jedoch prefetch_related verwenden, rufen Sie alle verwandten Objekte in einer zusätzlichen Abfrage ab, anstatt O (n) -Abfragen.

Da Sie nach Gebietsnamen sortieren möchten, sollten Sie wahrscheinlich das Modell Area für Ihr Abfrageset verwenden und dann die zugehörigen Rollen durchlaufen.

areas = Area.objects.filter(id__in=[1, 2, 3]).order_by('name').prefetch_related('role_set') 

for area in areas: 
    roles = area.role_set.all() 
    for role in roles: 
     print area.name, roles.name 

, dass Sie die Bestellung geben sollte, wollen, solange das Role Modell von name standardmäßig bestellt wird. Wenn nicht, könnten Sie ein Objekt Prefetch verwenden, um das zugehörige Abfrage-Set zu sortieren.

Verwandte Themen