/ / Django: comment filtrer () après distinct () - django

Django: comment filtrer () après distinct () - django

Si nous enchaînons un appel à filter () après un appel à distinct (), le filtre est appliqué à la requête avant le distinct. Comment filtrer les résultats d'une requête après appliquer distincte?

Example.objects.order_by("a","foreignkey__b").distinct("a").filter(foreignkey__b="something")

La clause where dans le SQL résultant de filter () signifie que le filtre est appliqué à la requête avant le distinct. Je veux filtrer le queryset résultant du distinct.

C’est probablement assez facile, mais je ne peux tout simplement pas comprendre et je ne peux rien trouver dessus.

Edit 1:

J'ai besoin de faire ça dans l'ORM ...

SELECT z.column1, z.column2, z.column3 FROM ( SELECT DISTINCT ON (b.column1, b.column2) b.column1, b.column2, c.column3 FROM table1 a INNER JOIN table2 b ON ( a.id = b.id ) INNER JOIN table3 c ON ( b.id = c.id) ORDER BY b.column1 ASC, b.column2 ASC, c.column4 DESC ) z WHERE z.column3 = "Something";

(J'utilise Postgres au fait.) Donc, je suppose que ce que je demande, c’est "Comment nichez-vous les sous-requêtes dans l’ORM? Est-ce possible?" Je vérifierai la documentation.

Désolé si je n'étais pas spécifique plus tôt. Ce n'était pas clair dans ma tête.

Réponses:

2 pour la réponse № 1

C'est une vieille question, mais lorsque vous utilisez Postgres, vous pouvez procéder comme suit pour forcer les requêtes imbriquées sur vos lignes "Distinctes":

foo = Example.objects.order_by("a","foreign_key__timefield").distinct("a")
bar = Example.objects.filter(pk__in=foo).filter(some_field=condition)

bar est la requête imbriquée telle que demandée dans OP sans recourir à raw / extra etc. Testé en travaillant dans 1.10 mais la documentation suggère que cela devrait fonctionner à au moins 1.7.

Mon cas d'utilisation était de filtrer une relation inverse. Si Example a une ForeignKey pour modéliser Toast, alors vous pouvez faire:

Toast.objects.filter(pk__in=bar.values_list("foreign_key",flat=true))

Cela vous donne toutes les instances de Toast où l'exemple associé le plus récent répond à vos critères de filtre.

Gros avertissement sur les performances, si vous utilisez cette barre, cela risque d’être un énorme ensemble de requêtes qui vous mènera probablement à un mauvais moment.


1 pour la réponse № 2

Non, vous ne pouvez pas faire cela en un seul SELECT.
Comme vous l'avez dit dans des commentaires, dans Django ORM filter est mappé à la clause SQL WHERE, et distinct orienté vers DISTINCT. Et dans un SQL, DISTINCT arrive toujours après WHERE en opérant sur le jeu de résultats, voir Doc SQLite par exemple.

Mais vous pouvez écrire une sous-requête pour imbriquer des SELECT, cela dépend de la cible réelle (je ne sais pas exactement ce qui vous appartient maintenant. Pourriez-vous la développer davantage?)

En outre, pour votre requête, distinct("a") ne conserve que la première occurrence de Example ayant le même a, Est-ce que c'est ce que tu veux?


1 pour la réponse № 3

Merci beaucoup pour l'aide les gars. J'ai essayé les deux suggestions et je ne pouvais en tirer aucune solution, mais je pense que cela m'a permis d'aller dans la bonne direction.

J'ai fini par utiliser

from django.db.models import Max, F

Example.objects.annotate(latest=Max("foreignkey__timefield")).filter(foreignkey__timefield=F("latest"), foreign__a="Something")

Ceci vérifie ce que le dernier foreignkey__timefieldest pour chaque exemple, et si c'est le dernier et a = quelque chose alors le garder. Si ce n'est pas le plus récent ou un! = Quelque chose pour chaque exemple, il est filtré.

Cela n'emboîte pas les sous-requêtes mais me donne le résultat recherché - et c'est assez simple. S'il y a moyen plus simple, j'aimerais vraiment savoir.