python - create - ¿Cómo consultar como GROUP BY en django?
filter django (9)
Yo pregunto un modelo,
Members.objects.all()
y devuelve decir
Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop
Lo que quiero es conocer la mejor manera de Django de lanzar una consulta group_by a mi db, como
Members.objects.all().group_by(''designation'')
Lo que no funciona, por supuesto. Sé que podemos hacer algunos trucos en "django / db / models / query.py", pero tengo curiosidad por saber cómo hacerlo sin parches.
El documento dice que puede usar valores para agrupar el conjunto de consultas.
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
Puedes encontrar todos los libros y agruparlos por nombre usando este código:
Book.objects.values(''name'').annotate(Count(''id'')).order_by() # ensure you add the order_by()
Puedes ver algunas hojas de cheet here .
Hay un módulo que le permite agrupar modelos de Django y seguir trabajando con un QuerySet en el resultado: https://github.com/kako-nawao/django-group-by
Por ejemplo:
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)
''libro / libros.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 diferencia con las consultas básicas de Django de annotate
/ aggregate
es el uso de los atributos de un campo relacionado, por ejemplo, book.author.last_name
.
Si necesita las PK de las instancias que se han agrupado, agregue la siguiente anotación:
.annotate(pks=ArrayAgg(''id''))
NOTA: ArrayAgg
es una función específica de Postgres, disponible desde Django 1.9 en adelante: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg
Necesitas hacer SQL personalizado como se ejemplifica en este fragmento:
SQL personalizado a través de subconsulta
O en un administrador personalizado como se muestra en la documentación de Django en línea:
Si no me equivoco, puedes usarlo, cualquier conjunto de consulta .group_by = ['' campo '']
Si quiere hacer agregación, puede usar las características de agregación del ORM :
from django.db.models import Count
Members.objects.values(''designation'').annotate(dcount=Count(''designation''))
Esto resulta en una consulta similar a
SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation
y la salida sería de la forma
[{''designation'': ''Salesman'', ''dcount'': 2},
{''designation'': ''Manager'', ''dcount'': 2}]
También puede utilizar la etiqueta de plantilla de regroup
para agrupar por atributos. De la documentación:
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>
Se ve como esto:
- India
- Mumbai: 19,000,000
- Calcuta: 15,000,000
- Estados Unidos
- Nueva York: 20,000,000
- Chicago: 7,000,000
- Japón
- Tokio: 33,000,000
También funciona en QuerySet
s creo.
fuente: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#regroup
Una solución fácil, pero no de una manera adecuada es usar RAW-SQL:
http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql
Otra solución es usar la propiedad group_by:
query = Members.objects.all().query
query.group_by = [''designation'']
results = QuerySet(query=query, model=Members)
Ahora puede iterar sobre la variable de resultados para recuperar sus resultados. Tenga en cuenta que group_by no está documentado y puede cambiarse en una versión futura de Django.
Y ... ¿por qué quieres usar group_by? Si no usa la agregación, puede usar order_by para lograr un resultado similar.
Django no soporta grupo libre por consultas . Lo aprendí de la muy mala manera. ORM no está diseñado para admitir cosas como lo que quieres hacer, sin usar SQL personalizado. Usted está limitado a:
- RAW sql (es decir, MyModel.objects.raw ())
-
cr.execute
sentencias (y un análisis hecho a mano del resultado). -
.annotate()
(el grupo por oraciones se realiza en el modelo secundario para .annotate (), en ejemplos como la agregación de lines_count = Count (''lines''))).
A través de una consulta qs
puede llamar a qs.query.group_by = [''field1'', ''field2'', ...]
pero es arriesgado si no sabe qué consulta está editando y no tiene garantía de que funcionará y No romper las partes internas del objeto QuerySet. Además, es una API interna (no documentada) a la que no debe acceder directamente sin correr el riesgo de que el código ya no sea compatible con las futuras versiones de Django.
from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))
Primero necesitas importar Suma luego ..