2013-03-25 11 views
8

Ich benutze Djangos eingebautes Benutzermodell und habe ein benutzerdefiniertes Foo-Objekt mit einem ForeignKey an Benutzer. Ich bin auf der Suche alle Benutzerobjekte und alle die Foo Objekte auszuwählen, die bestimmte Einschränkungen passen, etwa so:Django linken äußeren Join mit Filter

SELECT * from auth_user LEFT OUTER JOIN "foo" ON 
(auth_user.id = foo.id AND <other criteria here>) 

Wie soll ich dies in Django erreichen? Bisher habe ich versucht:

User.objects.filter(foo__<criteria>) 

aber das erzeugt SQL ähnlich wie diese:

SELECT * from auth_user LEFT OUTER JOIN "foo" ON 
(auth_user.id = foo.id) WHERE <other criteria here> 

und liefert nur Objekte Benutzer, die Foo-Objekte haben, die den Kriterien entsprechen. Alternativ kann ich alle Benutzerobjekte auswählen und für jede eine Abfrage ausführen, aber das wäre wesentlich weniger effizient.

+0

Was sind die anderen Kriterien? Ist es immer noch mit dem 'Foo'-Modell verbunden? – Ngenator

+0

Ja. Foo enthält ein paar Datumsfelder und ich wähle nur Foos aus einem bestimmten Datumsbereich. – Phobophobia

+0

Was soll 'User.objects.filter (foo__ ) 'tun? 'foo' ist kein Feld im' User'-Modell. Ich war nie in der Lage, eine Abfrage zu generieren, die 'LEFT OUTER JOIN' hat. – user2233706

Antwort

1

Wenn Sie Django wollte die alle holen User Objekte und alle Foo Objekte, die mit einem Benutzerobjekt verbunden sind, dann würden Sie select_related() verwenden:

User.objects.all().select_related('foo') 

aber hier wollen Sie nicht alle Die Foo Objekte, die zu einem Benutzerobjekt gehören, möchten Sie nur die Teilmenge von ihnen, die Ihre Kriterien erfüllen. Ich weiß nicht, wie ich Django sagen kann, dass er das in einem einzigen Abfrage-Set tun soll. Aber was Sie können tun sind sowohl separat zu tun wählt und treten die in Python:

# Map from user id to corresponding Foo satisfying <criteria>, if any. 
foos = {foo.user_id: foo for foo in 
     Foo.objects.filter(user__isnull = False, <criteria>)} 
for user in User.objects.all(): 
    foo = foos.get(user.id) 
    # ... 

(Dies gilt nicht mehr Datenbank arbeiten oder übertragen mehr Daten als Ihre LEFT OUTER JOIN würde, so dass ich denke, es ist ein vernünftiger Ansatz)

+0

Perfekt, danke :) – Phobophobia

+1

Denken Sie daran, dass ich nicht wirklich weiß, wie Ihre Modelle verwandt sind, so dass Sie möglicherweise meine vorgeschlagene Lösung ein wenig anpassen müssen, um Ihre Bedürfnisse zu erfüllen. –

+0

Ihre Lösung führt diese Abfrage aus: SELECT ••• FROM "auth_user" WHERE "auth_user". "Id" = für jeden Benutzer, der über ein Foo verfügt, das die Kriterien erfüllt. Soweit ich das beurteilen kann, hat das mit "{foo.user_id: foo" zu tun; Wenn Sie einen Teil durch etwas anderes ersetzen, werden die Abfragen nicht ausgeführt. Die einzige Beziehung zwischen den beiden Modellen ist, dass Foo einen ForeignKey hat, der auf Benutzer zeigt. – Phobophobia

1

es ist klobig, aber das sollte es tun.

User.objects.raw('SELECT auth_user.* from auth_user LEFT OUTER JOIN ' 
       '"foo" ON (auth_user.id = foo.id AND ' 
       '<other criteria here>)') 

Sie durch zusätzliche Attribute auf den zurückgegebenen Benutzerobjekte vollzuspritzen können sie auf der linken Seite des Auswahl hinzufügen.

0

Um ein LEFT OUTER JOIN Sie gehen können:

User.objects.select_related('foo').filter(Q(foo__isnull=True) | Q(<other criteria here>)) 

Django die foo__isnull=True verwendet es zu lenken einen LEFT OUTER JOIN zu erzeugen. Wenn foo__isnull=False ausgegeben wird, wird INNER JOIN wie ohne den Filterparameter generiert.

Verwandte Themen