Sélectionnez des valeurs distinctes dans un champ de table

105

J'ai du mal à comprendre l'ORM du Django. Ce que je veux faire, c'est obtenir une liste de valeurs distinctes dans un champ de ma table ... l'équivalent de l'un des éléments suivants:

SELECT DISTINCT myfieldname FROM mytable

(Ou bien)

SELECT myfieldname FROM mytable GROUP BY myfieldname

J'aimerais au moins le faire à la manière Django avant de recourir à SQL brut. Par exemple, avec une table:

id, rue, ville

1, rue Main, Hull

2, autre rue, Hull

3, Bibble Way, Leicester

4, une autre façon, Leicester

5, rue principale, Londidium

J'aimerais avoir:

Hull, Leicester, Londidium.

alj
la source

Réponses:

204

Dites que votre modèle est 'Shop'

class Shop(models.Model):
    street = models.CharField(max_length=150)
    city = models.CharField(max_length=150)

    # some of your models may have explicit ordering
    class Meta:
        ordering = ('city')

Étant donné que l' attribut de Metaclasse peut être orderingdéfini, vous pouvez utiliser order_by()sans paramètres pour effacer tout ordre lors de l'utilisation distinct(). Voir la documentation sous order_by()

Si vous ne voulez pas que l'ordre soit appliqué à une requête, pas même l'ordre par défaut, appelez order_by () sans paramètres.

et distinct()dans la note où il traite des problèmes d'utilisation distinct()avec la commande.

Pour interroger votre base de données, il vous suffit d'appeler:

models.Shop.objects.order_by().values('city').distinct()

Il renvoie un dictionnaire

ou

models.Shop.objects.order_by().values_list('city').distinct()

Celui-ci renvoie un ValuesListQuerySetque vous pouvez lancer en un list. Vous pouvez également ajouter flat=Trueà values_listpour aplatir les résultats.

Voir aussi: Obtenir des valeurs distinctes de Queryset par champ

jujule
la source
29
En fait, cela fonctionne. Toutefois! Je n'ai pas pu le faire fonctionner sur tous mes modèles. Weidly, cela a fonctionné sur certains mais pas sur d'autres. Pour ceux qui ont une commande Meta, cela ne fonctionne pas. Par conséquent, vous devez d'abord effacer l'ordre sur l'ensemble de requêtes. models.Shop.objects.order_by (). values ​​('city'). distinct ()
alj
2
Il est important de noter que values_listcela ne renvoie pas réellement une liste. Il renvoie quelque chose comme un jeu de requêtes. J'ai trouvé utile de toujours utiliser list () autour des appels values_list.
dheerosaur
8
values_listrenvoie ValuesListQuerySet qui est un itérateur. La diffusion dans la liste peut être pratique, mais peut également affecter les performances lorsque toutes les lignes doivent être évaluées en même temps, en particulier avec de grands ensembles de données.
Peter Kilczuk
3
La Meta: ordering = ()"fonctionnalité" de django orm et objects.distinct()vs. objects.ordering().distinct()nous a causé des heures de confusion. Il devrait y avoir un autocollant d'avertissement de sécurité du consommateur sur ce produit;) Nous pouvons instituer une politique d'attribut de commande sans méta pour éviter de se gratter la tête à l'avenir.
plaques de cuisson
Vous pouvez désactiver la Metaclasse orderinget résoudre les problèmes avec distincten utilisant order_by()sans paramètres. C'est dans la documentation de l'API QuerySet sous order_by()" Si vous ne voulez pas qu'un ordre soit appliqué à une requête, pas même l'ordre par défaut, appelez order_by()sans paramètres. "
Mark Mikofski
11

En plus de la réponse toujours très pertinente de jujule , je trouve qu'il est assez important d'être également conscient des implications de order_by()sur les distinct("field_name")requêtes. Ceci est, cependant, une fonctionnalité uniquement Postgres!

Si vous utilisez Postgres et si vous définissez un nom de champ pour lequel la requête doit être distincte, il order_by()faut alors commencer par le même nom de champ (ou noms de champ) dans la même séquence (il peut y avoir plus de champs par la suite).

Remarque

Lorsque vous spécifiez des noms de champ, vous devez fournir un order_by () dans le QuerySet, et les champs de order_by () doivent commencer par les champs de distinct (), dans le même ordre.

Par exemple, SELECT DISTINCT ON (a) vous donne la première ligne pour chaque valeur de la colonne a. Si vous ne spécifiez pas d'ordre, vous obtiendrez une ligne arbitraire.

Si vous voulez par exemple extraire une liste de villes dans lesquelles vous connaissez des magasins, l'exemple de jujule devrait être adapté à ceci:

# returns an iterable Queryset of cities.
models.Shop.objects.order_by('city').values_list('city', flat=True).distinct('city')  
Ingofreyer
la source
2

Par exemple:

# select distinct code from Platform where id in ( select platform__id from Build where product=p)
pl_ids = Build.objects.values('platform__id').filter(product=p)
platforms = Platform.objects.values_list('code', flat=True).filter(id__in=pl_ids).distinct('code')
platforms = list(platforms) if platforms else []
Roger Sodré
la source