Джанго выбрать только строки с повторяющимися значениями полей


предположим, что у нас есть модель в django, определенная следующим образом:

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

поле Name не является уникальным и, следовательно, может иметь повторяющиеся значения. Мне нужно выполнить следующую задачу: Выберите все строки из модели, которые имеют по меньшей мере одно повторяющееся значение на

5 70

5 ответов:

попробуй:

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

это так близко, как вы можете сделать с Django. Проблема в том, что это вернет ValuesQuerySet С name и count. Однако затем вы можете использовать это для построения регулярного QuerySet путем подачи его обратно в другой запрос:

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])

Это было отклонено как редактировать. Так вот оно как лучше ответ

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

это вернет ValuesQuerySet со всеми повторяющимися именами. Однако затем вы можете использовать это для создания обычного набора запросов, возвращая его в другой запрос. Django orm достаточно умен, чтобы объединить их в один запрос:

Literal.objects.filter(name__in=dups)

дополнительные звонка .значения ('name') после вызова аннотации выглядят немного странно. Без этого подзапрос не выполняется. Дополнительные значения обманывают orm, выбирая только столбец name для подзапроса.

попробуйте использовать агрегация

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

Если вы используете PostgreSQL, вы можете сделать что-то вроде этого:

from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Func, Value

duplicate_ids = (Literal.objects.values('name')
                 .annotate(ids=ArrayAgg('id'))
                 .annotate(c=Func('ids', Value(1), function='array_length'))
                 .filter(c__gt=1)
                 .annotate(ids=Func('ids', function='unnest'))
                 .values_list('ids', flat=True))

это приводит к этому довольно простому SQL-запросу:

SELECT unnest(ARRAY_AGG("app_literal"."id")) AS "ids"
FROM "app_literal"
GROUP BY "app_literal"."name"
HAVING array_length(ARRAY_AGG("app_literal"."id"), 1) > 1

Если вы хотите получить только список имен, но не объекты, вы можете использовать следующий запрос

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