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.
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}]
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.
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
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:
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
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.
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 .
from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))
vous devez d'abord importer Somme puis. .
si Je ne me trompe pas, vous pouvez utiliser whatever-query-set .group_by=[' champ ']