variable template tag get_template dirs python django django-queryset

python - template - Cómo anotar Count con una condición en un conjunto de consultas de Django



templates dirs django (2)

Usando Django ORM, ¿se puede hacer algo como queryset.objects.annotate(Count(''queryset_objects'', gte=VALUE)) . ¿Me entiendes?

Aquí hay un ejemplo rápido para ilustrar una posible respuesta:

En un sitio web de Django, los creadores de contenido envían artículos y los usuarios habituales ven (es decir, leen) dichos artículos. Los artículos pueden publicarse (es decir, están disponibles para que todos los lean) o en modo borrador. Los modelos que representan estos requisitos son:

class Article(models.Model): author = models.ForeignKey(User) published = models.BooleanField(default=False) class Readership(models.Model): reader = models.ForeignKey(User) which_article = models.ForeignKey(Article) what_time = models.DateTimeField(auto_now_add=True)

Mi pregunta es: ¿Cómo puedo obtener todos los artículos publicados, ordenados por lectores únicos de los últimos 30 minutos? Es decir, quiero contar cuántas vistas distintas (únicas) obtuvo cada artículo publicado en la última media hora, y luego producir una lista de artículos ordenados por estas vistas distintas.

Lo intenté:

date = datetime.now()-timedelta(minutes=30) articles = Article.objects.filter(published=True).extra(select = { "views" : """ SELECT COUNT(*) FROM myapp_readership JOIN myapp_article on myapp_readership.which_article_id = myapp_article.id WHERE myapp_readership.reader_id = myapp_user.id AND myapp_readership.what_time > %s """ % date, }).order_by("-views")

Esto provocó el error: error de sintaxis en o cerca de "01" (donde "01" era el objeto datetime dentro de extra). No es mucho para seguir.


Para django> = 1.8

Usar agregación condicional :

from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=IntegerField(), )) )

Explicación: la consulta normal a través de sus artículos se numviews con el campo numviews . Ese campo se construirá como una expresión CASE / WHEN, envuelta por Count, que devolverá 1 para los criterios de coincidencia de lectores y NULL para los criterios de coincidencia de lectores. Count ignorará los valores nulos y solo contará valores.

Obtendrá ceros en los artículos que no se han visto recientemente y puede usar ese campo de numviews para ordenar y filtrar.

La consulta detrás de esto para PostgreSQL será:

SELECT "app_article"."id", "app_article"."author", "app_article"."published", COUNT( CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN 1 ELSE NULL END ) as "numviews" FROM "app_article" LEFT OUTER JOIN "app_readership" ON ("app_article"."id" = "app_readership"."which_article_id") GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"

Si queremos rastrear solo consultas únicas, podemos agregar distinción en Count y hacer que nuestra cláusula When devuelva valor, queremos distinguir en.

from django.db.models import Count, Case, When, CharField, F Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=F(''readership__reader'')), # it can be also `readership__reader_id`, it doesn''t matter output_field=CharField(), ), distinct=True) )

Eso producirá:

SELECT "app_article"."id", "app_article"."author", "app_article"."published", COUNT( DISTINCT CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN "app_readership"."reader_id" ELSE NULL END ) as "numviews" FROM "app_article" LEFT OUTER JOIN "app_readership" ON ("app_article"."id" = "app_readership"."which_article_id") GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"

Para django <1.8 y PostgreSQL

Puede usar raw para ejecutar la instrucción SQL creada por las versiones más recientes de django. Aparentemente, no existe un método simple y optimizado para consultar esos datos sin usar raw (incluso con extra hay algunos problemas con la inyección de la cláusula JOIN requerida).

Articles.objects.raw(''SELECT'' '' "app_article"."id",'' '' "app_article"."author",'' '' "app_article"."published",'' '' COUNT('' '' DISTINCT CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN "app_readership"."reader_id"'' '' ELSE NULL END'' '' ) as "numviews"'' ''FROM "app_article" LEFT OUTER JOIN "app_readership"'' '' ON ("app_article"."id" = "app_readership"."which_article_id")'' ''GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"'')


Para django> = 2.0 puede usar la agregación condicional con un argumento de filter en las funciones agregadas:

from datetime import timedelta from django.utils import timezone from django.db.models import Count, Q # need import Article.objects.annotate( numviews=Count( ''readership__reader__id'', filter=Q(readership__what_time__gt=timezone.now() - timedelta(minutes=30)), distinct=True ) )