web-dev-qa-db-fra.com

Sélectionnez DISTINCT des colonnes individuelles dans Django?

Je suis curieux de savoir s'il existe un moyen de faire une requête dans Django qui n'est pas un "SELECT * FROM... "dessous. J'essaie de faire un" SELECT DISTINCT columnName FROM ..." au lieu.

Plus précisément, j'ai un modèle qui ressemble à:

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

Rank est un rang dans un Category. J'aimerais pouvoir parcourir toutes les catégories en effectuant des opérations sur chaque rang de cette catégorie.

J'aimerais d'abord obtenir une liste de toutes les catégories du système, puis interroger tous les produits de cette catégorie et répéter l'opération jusqu'à ce que chaque catégorie soit traitée.

Je préférerais éviter le SQL brut, mais si je devais y aller, ça irait. Bien que je n’ai jamais codé SQL brut dans Django/Python auparavant.

80
jamida

Une façon d'obtenir la liste des noms de colonne distincts de la base de données consiste à utiliser distinct() en conjonction avec values() .

Dans votre cas, vous pouvez procéder comme suit pour obtenir les noms de catégories distinctes:

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"

Il y a quelques points à retenir ici. Tout d'abord, ceci retournera un ValuesQuerySet qui se comporte différemment d'un QuerySet. Lorsque vous accédez à say, le premier élément de q (ci-dessus), vous obtenez un dictionnaire, PAS une instance de ProductOrder.

Deuxièmement, ce serait une bonne idée de lire le note d’avertissement de la documentation concernant l’utilisation de distinct(). L'exemple ci-dessus fonctionnera, mais toutes les combinaisons de distinct() et values() risquent de ne pas l'être.

[~ # ~] ps [~ # ~] : c'est une bonne idée d'utiliser noms en minuscules pour les champs d'un modèle. Dans votre cas, cela impliquerait de réécrire votre modèle comme indiqué ci-dessous:

class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()
166
Manoj Govindan

C'est assez simple en fait si vous utilisez PostgreSQL, utilisez simplement distinct(columns).

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

Notez que cette fonctionnalité est incluse dans Django depuis la 1.4

44
Wolph

Les autres réponses sont correctes, mais c'est un peu plus propre, en ce sens que cela ne donne que les valeurs que vous obtiendriez d'une requête DISTINCT, sans aucune cruauté de la part de 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']

Et cela fonctionne sans PostgreSQL.

C'est moins efficace que d'utiliser un .distinct (), en supposant que DISTINCT dans votre base de données est plus rapide qu'un python set, mais c'est bien pour les nouilles autour de Shell.

15
Mark Chackerian

Commande utilisateur par avec ce champ, puis faire distinct.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
7
SuperNova