Comment interroger en groupe par in django?

j'interroge un modèle,

Members.objects.all()

et il retourne dire

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

ce que je veux, c'est connaître la meilleure façon Django de tirer une requête group_by à ma base de données, comme,

Members.objects.all().group_by('designation')

ça ne marche pas, bien sûr. Je sais que nous pouvons faire quelques trucs sur "django/db/models/query.py", mais je suis juste curieux de savoir comment le faire sans correction.

239
demandé sur Serenity 2009-03-10 13:10:30

9 réponses

si vous avez l'intention d'effectuer l'agrégation, vous pouvez utiliser les caractéristiques d'agrégation de L'ORM :

from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

il en résulte une requête similaire à

SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

et la sortie serait de la forme

[{'designation': 'Salesman', 'dcount': 2}, 
 {'designation': 'Manager', 'dcount': 2}]
371
répondu Guðmundur H 2017-02-17 14:47:48

une solution facile, mais pas d'une manière appropriée est D'utiliser RAW-SQL:

http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql

une autre solution consiste à utiliser la propriété group_by:

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

vous pouvez maintenant itérer sur la variable de résultats pour récupérer vos résultats. Notez que group_by n'est pas documenté et peut être modifié dans la future version de Django.

Et... pourquoi voulez-vous utiliser group_by? Si vous n'utilisez pas l'agrégation, vous pouvez utiliser order_by à obtenir un comme résultat.

39
répondu Michael 2012-07-13 14:21:41

vous pouvez également utiliser la balise de modèle regroup pour grouper par attributs. Extrait du docs:

cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}

<ul>
    {% for country in country_list %}
        <li>{{ country.grouper }}
            <ul>
            {% for city in country.list %}
                <li>{{ city.name }}: {{ city.population }}</li>
            {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

ressemble à ceci:

  • de l'Inde
    • Mumbai: 19 000 000 de
    • Calcutta: 15 000 000 de
  • USA
    • New York: 20,000,000
    • Chicago: 7 000 000 d'1519110920"
  • le Japon
    • Tokyo: 33,000,000

Il travaille également sur QuerySet s je crois.

source: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#regroup

8
répondu inostia 2017-11-16 00:54:17

vous devez faire du SQL personnalisé comme illustré dans cet extrait:

SQL Personnalisée via la sous-requête

ou dans un custom manager comme indiqué dans les Docs en ligne de Django:

ajout de méthodes supplémentaires de gestion

5
répondu Van Gale 2009-03-10 10:24:57

il y a un module qui vous permet de grouper des modèles Django et de continuer à travailler avec un QuerySet dans le résultat: https://github.com/kako-nawao/django-group-by

par exemple:

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

livre/des livres.html '

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

la différence par rapport aux requêtes de base annotate / aggregate Django est l'utilisation des attributs d'un champ associé, par exemple book.author.last_name .

si vous avez besoin des Pk des instances qui ont été regroupées, ajouter l'annotation suivante:

.annotate(pks=ArrayAgg('id'))

NOTE: ArrayAgg est une fonction spécifique de Postgres, disponible à partir de Django 1.9: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg

4
répondu Risadinha 2016-09-29 16:14:04

Django ne supporte pas le free group par les requêtes . Je l'ai appris dans le très mauvais sens du terme. ORM n'est pas conçu pour supporter des choses comme ce que vous voulez faire, sans utiliser le SQL personnalisé. Vous êtes limité à:

  • sql BRUT (c'est à dire Monmodèle.objet.raw ())
  • cr.execute phrases (et une analyse manuelle du résultat).
  • .annotate() (le groupe par Phrases est exécuté dans le modèle de l'enfant) pour. annoter(), dans des exemples comme l'agrégation lines_count=Count('lignes'))).

sur un queryset qs vous pouvez appeler qs.query.group_by = ['field1', 'field2', ...] mais c'est risqué si vous ne savez pas quelle requête vous éditez et n'avez aucune garantie qu'elle fonctionnera et ne cassera pas les internes de l'objet QuerySet. En outre, il s'agit d'une API interne (non documentée) à laquelle vous ne devez pas accéder directement sans risquer que le code ne soit plus compatible avec les futures versions de Django.

3
répondu Luis Masuelli 2015-11-12 18:06:20

le document dit que vous pouvez utiliser des valeurs pour grouper le queryset .

class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times, 
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True)) 
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had 
#  visited the interest for 2 times

vous pouvez trouver tous les livres et les regrouper par leur nom en utilisant ce code:

Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

vous pouvez regarder quelques feuilles de guépard ici .

0
répondu ramwin 2017-11-07 10:26:20
from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))

vous devez d'abord importer Somme puis. .

0
répondu Kiran S youtube channel 2018-09-24 03:51:14

si Je ne me trompe pas, vous pouvez utiliser whatever-query-set .group_by=[' champ ']

-1
répondu Reed Jones 2014-11-10 12:22:15