Подтвердить что ты не робот

Django выбирает только строки с двойными значениями поля

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

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

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

Я знаю, как это сделать, используя простой SQL (может быть, не лучшее решение):

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

Итак, можно ли это выбрать с помощью ORM django? Или лучше SQL-решение?

4b9b3361

Ответ 1

Try:

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

Это как можно ближе к Django. Проблема в том, что это вернет a 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])

Ответ 2

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

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

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

Literal.objects.filter(name__in=dups)

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

Ответ 3

попробуйте использовать aggregation

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

Ответ 4

Если вы используете 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

Ответ 5

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

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