/ / Force INNER JOIN pour Django Query - python, sql, django, orm

Force INNER JOIN pour Django Query - python, sql, django, orm

Voici mon schéma:

Ville -> Photographe

J'essaie d 'obtenir une liste des villes qui ont au moins un photographe et renvoie le nombre de photographes pour les villes.

Voici le queryset avec lequel je travaille:

City.objects.annotate(photographer_count=aggregates.Count("photographers")).filter(photographer_count__gt=0).order_by("-photographer_count")

Cela fonctionne exactement comme je l’attendais aussi,Django choisit de faire la jonction entre ville / photographe avec une jointure externe gauche, sauf pour une raison quelconque. Si je prends le texte SQL et modifie simplement le "extérieur gauche" en "intérieur", la requête passe de ~ 11 secondes à 200 ms avec des résultats identiques.

J’ai essayé de placer un filtre devant l’annotation pour indiquer à Django que ce devrait être une jonction interne, mais cela n’a pas fonctionné.

Toute requête Django voodoo que je peux effectuer sur cela pour obtenir cette jointure interne? Je me rends compte que je peux utiliser directement SQL, mais préférerais passer par l'ORM.

Réponses:

6 pour la réponse № 1

Par défaut, un LEFT JOIN est généré pour que Django puisse vous générer des lignes, même pour les villes sans photographes. Si vous connaître vous ne voulez pas ceux-ci, voici une astuce pour forcer Django à générer un INNER JOIN:

City.objects.filter(
photographer__isnull=False
).annotate(
photographer_count=aggregates.Count("photographers")
).filter(
photographer_count__gt=0
).order_by(
"-photographer_count"
)

Plus précisément, ce premier filtre indique à Django que INNER JOIN est sécurisé. Il doit venir avant le annotate() appel.


1 pour la réponse № 2

Vous ne pouvez généralement pas obtenir ce niveau de contrôle sur les requêtes et les jointures exécutées par Django.

Django ne fait pas des choses vraiment intelligentes commeremarquez que la condition de filtre signifie que vous pouvez vous en tirer avec un INNER JOIN à ce stade - il doit générer des requêtes correctes dans le cas général, ce qui dans ce cas signifie un LEFT OUTER JOIN, pour autant que je sache.

Donc, vous allez probablement avoir besoin de SQL brut.