suponha que temos um modelo em Django definido da seguinte forma:
class Literal:
name = models.CharField(...)
...
O campo de nome não é exclusivo e, portanto, pode ter valores duplicados. Preciso realizar a seguinte tarefa: Selecionar todas as linhas do modelo que tenham pelo menos um valor duplicado do name
campo.
Eu sei fazer isso usando SQL simples (pode não ser a melhor solução):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
Então, é possível selecionar isso usando django ORM? Ou melhor solução SQL?
sql
django
django-orm
dragão
fonte
fonte
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
anotação a ser salva, o padrão é[field]__count
. No entanto, essa sintaxe de sublinhado duplo também é a forma como o Django interpreta que você deseja fazer uma junção. Então, essencialmente quando você tenta filtrar isso, Django pensa que você está tentando fazer uma junção com acount
qual obviamente não existe. A correção é especificar um nome para o resultado da sua anotação, ou seja,annotate(mycount=Count('id'))
e então filtrarmycount
.values('name')
depois de sua chamada para anotar, você pode remover a compreensão da lista e dizer oLiteral.objects.filter(name__in=dupes)
que permitirá que tudo seja executado em uma única consulta.Isso foi rejeitado como uma edição. Então aqui está como uma resposta melhor
Isso retornará um
ValuesQuerySet
com todos os nomes duplicados. No entanto, você pode usar isso para construir um regularQuerySet
alimentando-o de volta em outra consulta. O django ORM é inteligente o suficiente para combiná-los em uma única consulta:A chamada extra para
.values('name')
após a chamada de anotação parece um pouco estranha. Sem isso, a subconsulta falha. Os valores extras enganam o ORM, fazendo-o selecionar apenas a coluna de nome para a subconsulta.fonte
.order_by()
?GROUP BY
cláusula SQL e quebrará as coisas. Descobri isso ao jogar com Subquery (na qual você faz agrupamentos muito semelhantes via.values()
)tente usar agregação
fonte
Caso você use PostgreSQL, você pode fazer algo assim:
Isso resulta nesta consulta SQL bastante simples:
fonte
Se você deseja resultar apenas na lista de nomes, mas não nos objetos, você pode usar a seguinte consulta
fonte