предположим, что у нас есть модель в 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
);
Итак, можно ли выбрать это с помощью django ORM? Или лучшее решение SQL?
sql
django
django-orm
драгуна
источник
источник
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
аннотации, которая будет сохранена как, по умолчанию будет использоваться[field]__count
. Однако этот синтаксис с двойным подчеркиванием также интерпретирует Django, что вы хотите выполнить соединение. Итак, по сути, когда вы пытаетесь отфильтровать это, Django думает, что вы пытаетесь выполнить соединение,count
которого явно не существует. Исправление состоит в том, чтобы указать имя для результата аннотации, т.е. вместоannotate(mycount=Count('id'))
этого включить фильтрациюmycount
.values('name')
после вашего вызова для аннотирования, вы можете удалить понимание списка и сказать,Literal.objects.filter(name__in=dupes)
что позволит всем этим выполняться в одном запросе.Это было отклонено как правка. Итак, вот как лучший ответ
Это вернет
ValuesQuerySet
со всеми повторяющимися именами. Однако затем вы можете использовать это для создания регулярногоQuerySet
запроса, передав его обратно в другой запрос. ORM django достаточно умен, чтобы объединить их в один запрос:Дополнительный вызов
.values('name')
после вызова аннотации выглядит немного странно. Без этого подзапрос завершится ошибкой. Дополнительные значения заставляют ORM выбирать только столбец имени для подзапроса.источник
.order_by()
?GROUP BY
, и это ломает вещи. Обнаружил это, играя с подзапросом (в котором вы выполняете очень похожую группировку через.values()
)попробуйте использовать агрегацию
источник
Если вы используете PostgreSQL, вы можете сделать что-то вроде этого:
В результате получается довольно простой 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
источник
Если вы хотите получить только список имен, но не объекты, вы можете использовать следующий запрос
источник