2009-12-05 15 views
15

Esto no parece funcionar en Django 1.1 (creo que esto requerirá una subconsulta, por lo tanto, viene el título)Django anotar conjunto problema con un recuento de sub consulta

qs.annotate(interest_level= \ 
      Count(Q(tags__favoritedtag_set__user=request.user)) 
      ) 

Hay artículos en mi consulta establecidos que están etiquetados y las etiquetas pueden ser preferidas por los usuarios, me gustaría calcular cuántas veces un usuario ha favorecido cada elemento en el conjunto a través de etiquetas.

¿hay alguna manera de construir una consulta como esta sin usar extra()?

Gracias.

+0

¿Aparece un mensaje de error? ¿Podría proporcionar los modelos con los que está trabajando? – cethegeek

+0

el error es "excepción 'Q' objeto no tiene atributo 'división'", geradeausanwalt es correcto agregación funcs no toman objetos Q como argumentos. Los modelos en su respuesta son similares a los míos. – Evgeny

Respuesta

10

En cuanto a la función add_aggregate dentro de django/db/models/sql/query.py, los objetos de consulta no se aceptarán como valores de entrada.

Desafortunadamente, actualmente no hay una forma directa dentro de Django para agregar/anotar en lo que equivale a un conjunto de preguntas, especialmente no uno que se filtra de alguna manera.

Suponiendo los siguientes modelos:

class Item(models.Model): 
    name = models.CharField(max_length=32) 

class Tag(models.Model): 
    itemfk = models.ForeignKey(Item, related_name='tags') 
    name = models.CharField(max_length=32) 

class FavoritedTag(models.Model): 
    user = models.ForeignKey(User) 
    tag = models.ForeignKey(Tag) 

Además, no se pueden anotar un conjunto de consultas en los campos definidos a través de .extra().

Uno podría caer en SQL en views.py así:

from testing.models import Item, Tag, FavoritedTag 
from django.shortcuts import render_to_response 
from django.contrib.auth.decorators import login_required 
from django.utils.datastructures import SortedDict 

@login_required 
def interest_level(request): 
    ruid = request.user.id 

    qs = Item.objects.extra(
     select = SortedDict([ 
      ('interest_level', 'SELECT COUNT(*) FROM testing_favoritedtag, testing_tag \ 
      WHERE testing_favoritedtag.user_id = %s \ 
      AND testing_favoritedtag.tag_id = testing_tag.id \ 
      AND testing_tag.itemfk_id = testing_item.id'), 
     ]), 
     select_params = (str(ruid),) 
    ) 

    return render_to_response('testing/interest_level.html', {'qs': qs}) 

Plantilla:

{% for item in qs %} 
    name: {{ item.name }}, level: {{ item.interest_level }}<br> 
{% endfor %} 

Probé esto utilizando MySQL5. Sin embargo, dado que no soy experto en SQL, me gustaría saber cómo optimizar aquí, o si hay otra forma de "disminuir" la cantidad de SQL. ¿Tal vez hay alguna forma interesante de utilizar la característica related_name aquí directamente en SQL?

1

Si quiere evitar caer al SQL sin procesar, otra forma de despellejar a este gato sería usar un método modelo, que le dará un nuevo atributo en el modelo para usar en sus plantillas. No probado, pero algo como esto en sus etiquetas modelo debería funcionar:

class Tag(models.Model): 
    itemfk = models.ForeignKey(Item, related_name='tags') 
    name = models.CharField(max_length=32) 

    def get_favetag_count(self): 
     """ 
     Calculate the number of times the current user has favorited a particular tag 
     """ 

     favetag_count = FavoritedTag.objects.filter(tag=self,user=request.user).count() 
     return favetag_count 

Luego, en su plantilla se puede usar algo como:

{{tag}} ({{tag.get_favetag_count}}) 

La desventaja de este enfoque es que se podría llegar a la base de datos más si estás en un gran círculo o algo así. Pero, en general, funciona bien y evita la incapacidad de hacer anotaciones para realizar consultas en modelos relacionados. Y evita tener que usar SQL sin procesar.

+5

No es equivalente a las anotaciones, eso es terriblemente ineficiente. – hcalves

Cuestiones relacionadas