supposons que nous ayons un modèle dans django défini comme suit:
class Literal:
name = models.CharField(...)
...
Le champ Nom n'est pas unique et peut donc avoir des valeurs en double. Je dois accomplir la tâche suivante: Sélectionnez toutes les lignes du modèle qui ont au moins une valeur en double du name
champ.
Je sais comment le faire en utilisant SQL brut (ce n'est peut-être pas la meilleure solution):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
Alors, est-il possible de sélectionner ceci en utilisant django ORM? Ou une meilleure solution SQL?
sql
django
django-orm
dragon
la source
la source
Literal.objects.values('name').annotate(name_count=Count('name')).filter(name_count__gt=1)
?Cannot resolve keyword 'id_count' into field
values_list('name', flat=True)
Count
annotation à enregistrer sous, il est par défaut[field]__count
. Cependant, cette syntaxe à double trait de soulignement est également la façon dont Django interprète votre souhait de faire une jointure. Donc, essentiellement lorsque vous essayez de filtrer sur cela, Django pense que vous essayez de faire une jointure aveccount
laquelle il n'existe manifestement pas. Le correctif consiste à spécifier un nom pour le résultat de votre annotation, c'estannotate(mycount=Count('id'))
-à- dire puis à filtrer à lamycount
place.values('name')
après votre appel à annoter, vous pouvez supprimer la compréhension de la liste et direLiteral.objects.filter(name__in=dupes)
ce qui permettra à tout cela d'être exécuté en une seule requête.Cela a été rejeté en tant que modification. Alors voici une meilleure réponse
Cela renverra un
ValuesQuerySet
avec tous les noms en double. Cependant, vous pouvez ensuite l'utiliser pour construire un régulierQuerySet
en le réinjectant dans une autre requête. L'ORM django est suffisamment intelligent pour les combiner en une seule requête:L'appel supplémentaire
.values('name')
après l'appel d'annotation semble un peu étrange. Sans cela, la sous-requête échoue. Les valeurs supplémentaires incitent l'ORM à ne sélectionner que la colonne de nom pour la sous-requête.la source
.order_by()
pour?GROUP BY
clause SQL , et cela brise les choses. J'ai découvert cela en jouant avec Subquery (dans lequel vous faites un regroupement très similaire via.values()
)essayez d'utiliser l' agrégation
la source
Si vous utilisez PostgreSQL, vous pouvez faire quelque chose comme ceci:
Il en résulte cette requête SQL assez simple:
la source
Si vous souhaitez obtenir uniquement une liste de noms mais pas d'objets, vous pouvez utiliser la requête suivante
la source