Selecione DISTINTO colunas individuais no django?

93

Estou curioso para saber se há alguma maneira de fazer uma consulta no Django que não seja um " SELECT * FROM..." subjacente. Estou tentando fazer um " SELECT DISTINCT columnName FROM ..." em vez disso.

Especificamente, tenho um modelo que se parece com:

class ProductOrder(models.Model):
   Product  = models.CharField(max_length=20, promary_key=True)
   Category = models.CharField(max_length=30)
   Rank = models.IntegerField()

onde o Ranké uma classificação dentro de a Category. Eu gostaria de poder iterar em todas as categorias, fazendo alguma operação em cada classificação dentro dessa categoria.

Eu gostaria primeiro de obter uma lista de todas as categorias do sistema e, em seguida, consultar todos os produtos dessa categoria e repetir até que todas as categorias sejam processadas.

Prefiro evitar SQL bruto, mas se tiver que ir lá, tudo bem. Embora eu nunca tenha codificado SQL bruto em Django / Python antes.

Jamida
fonte

Respostas:

185

Uma maneira de obter a lista de nomes de colunas distintos do banco de dados é usar distinct() em conjunto com values().

No seu caso, você pode fazer o seguinte para obter os nomes de categorias distintas:

q = ProductOrder.objects.values('Category').distinct()
print q.query # See for yourself.

# The query would look something like
# SELECT DISTINCT "app_productorder"."category" FROM "app_productorder"

Há algumas coisas a serem lembradas aqui. Primeiro, isso retornará a, ValuesQuerySetque se comporta de maneira diferente de a QuerySet. Quando você acessa, digamos, o primeiro elemento de q(acima), você obterá um dicionário , NÃO uma instância de ProductOrder.

Em segundo lugar, seria uma boa ideia ler a nota de aviso nos documentos sobre o uso distinct(). O exemplo acima funcionará, mas todas as combinações de distinct()e values()podem não funcionar.

PS : é uma boa ideia usar nomes em minúsculas para os campos em um modelo. No seu caso, isso significaria reescrever seu modelo conforme mostrado abaixo:

class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()
Manoj Govindan
fonte
1
O método descrito abaixo agora está disponível no django 1.4 e é bom se você precisar da instância ProductOrder com reconhecimento de campo distinto ;-)
Jonathan Liuti
60

Na verdade, é muito simples se você estiver usando PostgreSQL , basta usar distinct(columns)( documentação ).

Productorder.objects.all().distinct('category')

Observe que este recurso foi incluído no Django desde 1.4

Wolph
fonte
@lazerscience, @Manoj Govindan: Sinto muito, você está certo. Parece que apliquei um patch no Django para adicionar esse recurso. Eu adicionei um link para o patch
Wolph,
3
Agora está no Django SVN e estará no Django 1.4
Will Hardy
14
Nota: a menos que você esteja usando PostgreSQL, você não pode fornecer um argumento distinto (). Melhor ficar com a solução aceita acima.
Mark Chackerian
nos testes, isso can_distinct_on_fieldsparece ser apenas Postgres
Skylar Saveland
3
mais 1, mas all()não é necessário aqui
Antony Hatchkins,
17

As outras respostas são boas, mas isso é um pouco mais claro, pois fornece apenas os valores como você obteria de uma consulta DISTINCT, sem qualquer sujeira do Django.

>>> set(ProductOrder.objects.values_list('category', flat=True))
{u'category1', u'category2', u'category3', u'category4'}

ou

>>> list(set(ProductOrder.objects.values_list('category', flat=True)))
[u'category1', u'category2', u'category3', u'category4']

E funciona sem PostgreSQL.

Isso é menos eficiente do que usar um .distinct (), presumindo que DISTINCT em seu banco de dados seja mais rápido do que um python set, mas é ótimo para mexer no shell.

Mark Chackerian
fonte
values_listnão coloca DISTINCTna consulta sql, então isso traria vários valores se houvesse.
mehmet
12

O usuário ordena com esse campo e, em seguida, faz distinção.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
Super Nova
fonte