141 votes

Django sélectionne uniquement les lignes avec des valeurs de champ en double

supposons que nous avons un modèle dans django définis comme suit:

class Literal:
    name = models.CharField(...)
    ...

Nom de domaine n'est pas unique, et peuvent ainsi avoir des valeurs en double. J'ai besoin pour accomplir la tâche suivante: Sélectionnez toutes les lignes du modèle qui ont au moins un double de la valeur de l' name champ.

Je sais comment le faire en utilisant la plaine SQL (peut-être pas la meilleure solution):

select * from literal where name IN (
    select name from literal group by name having count((name)) > 1
);

Donc, est-il possible de sélectionner cette utilisation de l'ORM de django? Ou mieux SQL solution?

286voto

Chris Pratt Points 53859

Essayer:

 from django.db.models import Count
Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1)
 

C’est aussi proche que possible de Django. Le problème est que cela retournera ValuesQuerySet avec seulement name et count . Cependant, vous pouvez ensuite l'utiliser pour construire un QuerySet normal en le ré-alimentant dans une autre requête:

 dupes = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1)
Literal.objects.filter(name__in=[item['name'] for item in dupes])
 

67voto

Aaron Merriam Points 566

Cela a été rejeté comme une modification. Si elle est ici comme meilleure réponse

Literal.objects.values('name').annotate(count=Count('id')).values('name').order_by().filter(count__gt=1)

Cela renvoie un ValuesQuerySet avec tous les noms en double. Cependant, vous pouvez ensuite l'utiliser pour construire un régulier QuerySet en le nourrissant de nouveau dans une autre requête. L'orm de django est assez intelligent pour les combiner en une seule requête:

dupes = Literal.objects.values('name').annotate(Count('id')).values('name').order_by().filter(id__count__gt=1)
Literal.objects.filter(name__in=dupes)

L'appel supplémentaire à .values('nom') après l'annoter appel a l'air un peu étrange. Sans cela, la sous-requête échoue. Les valeurs des astuces de l'orm en sélectionnant uniquement la colonne nom de la sous-requête.

12voto

JamesO Points 7085

essayez d'utiliser l' agrégation

 Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
 

0voto

user2959723 Points 1

Si vous voulez obtenir uniquement une liste de noms mais pas d'objets, vous pouvez utiliser la requête suivante

 repeated_names = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1).values_list('name', flat='true')
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X