2009-09-14 7 views
9

je les modèles, plus ou moins comme ceci:Django LEFT JOIN?

class ModelA(models.Model): 
    field = models.CharField(..) 

class ModelB(models.Model): 
    name = models.CharField(.., unique=True) 
    modela = models.ForeignKey(ModelA, blank=True, related_name='modelbs') 

    class Meta: 
     unique_together = ('name','modela') 

Je veux faire une requête qui dit quelque chose comme: « Obtenez tous le nom du champ où est modela égale à X qui ont un modèle ModelB avec un nom de X OU sans nom de modèle du tout »

jusqu'à présent, j'ai ceci:

cela me obtenir tous les ModelAs qui ont au moins un modelB (et en réalité, ce sera toujours un seul) - mais si un ModelA n'a pas de ModelBs, il ne sera pas t être dans le jeu de résultats. J'ai besoin d'être dans le resultset avec quelque chose comme obj.modelb = Aucun

Comment puis-je accomplir cela?

+3

Sur une note côté: Ce serait vraiment aider si vous avez utilisé des noms descriptifs comme scénario Blog/Post typique ou au moins Foo/Bar au lieu de ModelA/ModelB qui sont non intuitives et simplement difficiles à lire/distinguer. –

Répondre

11

Utilisez Q pour combiner les deux conditions suivantes:

from django.db.models import Q 
qs = ModelA.objects.exclude(field=condition) 
qs = qs.filter(Q(modelbs__name=condition) | Q(modelbs__isnull=True)) 

Pour examiner la requête SQL résultant:

print qs.query.as_sql() 

Sur une requête similaire, cela génère en LEFT ... WHERE (un .val = b OU a.id EST NULL).

+0

Ça ne fait pas de différence pour moi –

+3

Si tout ce que vous allez dire est "ça ne marche pas", je ne peux certainement pas vous aider. Avez-vous même examiné le SQL? –

+0

Oui, je l'ai fait. Désolé je n'étais pas plus précis J'étais occupé à essayer une suggestion que j'ai trouvée ailleurs. Quoi qu'il en soit, faire ce que vous avez fait est que la condition de jointure soit sur la clause where par opposition à la clause ON, je ne sais pas exactement pourquoi mais cela fait que la jointure gauche ne se comporte pas comme prévu. Si je lance manuellement la même requête avec la condition déplacée vers ON, cela fonctionne comme je le souhaite. –

-3

LEFT JOIN est une union de deux requêtes. Parfois, il est optimisé pour une requête. Parfois, il n'est pas réellement optimisé par le moteur SQL sous-jacent et se fait en deux requêtes distinctes.

Pour ce faire. Ne pas fétichiser sur les jointures externes SQL semblant être une requête "unique".

+5

Ceci est ans, mais je tiens à noter, des conseils terribles. Cela finit par exécuter un grand nombre de requêtes dans les coulisses, et peut facilement amener votre site à une exploration. –

1

On dirait que vous faites face à la barrière des 80%. Pourquoi ne pas simplement utiliser .extra(select={'has_x_or_none':'(EXISTS (SELECT ...))'}) pour effectuer une sous-requête? Vous pouvez écrire la sous-requête comme vous le souhaitez et vous devriez pouvoir filtrer le nouveau champ. Le SQL doit liquider à la recherche quelque chose comme ceci:

SELECT *, 
    ((EXISTS (SELECT * FROM other WHERE other.id=primary.id AND other.name='X')) 
    OR (NOT EXISTS (SELECT * FROM other WHERE other.id=primary.id))) AS has_x_or_none 
    FROM primary WHERE has_x_or_none=1; 
Questions connexes