Estoy trabajando en un proyecto de Django en el que tengo un conjunto de consultas de objetos 'A' ( A.objects.all() ), y necesito anotar varios campos de una subconsulta de objetos 'B'. El problema es que el método de anotación solo puede manejar un tipo de campo por parámetro (DecimalField, CharField, etc.), por lo tanto, para anotar múltiples campos, debo usar algo como:
A.objects.all().annotate(b_id =Subquery(B_queryset.values('id')[:1], b_name =Subquery(B_queryset.values('name')[:1], b_other_field =Subquery(B_queryset.values('other_field')[:1], ... )Lo cual es muy ineficiente, ya que crea una nueva subconsulta/subselección en el SQL final para cada campo que quiero anotar. Me gustaría usar la misma subselección con varios campos en sus parámetros de valores (), y anotarlos todos en el conjunto de consultas de A. Me gustaría usar algo como esto:
b_subquery = Subquery(B_queryset.values('id', 'name', 'other_field', ...)[:1]) A.objects.all().annotate(b=b_subquery) Pero cuando trato de hacer eso (y accedo al primer elemento A.objects.all().annotate(b=b_subquery)[0] ) genera una excepción:
{FieldError}Expression contains mixed types. You must set output_field.
Y si configuro Subquery(B_quer...[:1], output_field=ForeignKey(B, models.DO_NOTHING)) , obtengo una excepción de base de datos:
{ProgrammingError}subquery must return only one column
En pocas palabras, todo el problema es que tengo varias B que "pertenecen" a una A, por lo que necesito usar Subquery para, por cada A en A.objects.all() , elegir una B específica y adjuntarla a esa A, usando OuterRefs y algunos filtros (solo quiero algunos campos de B), lo que me parece un problema trivial.
¡Gracias por cualquier ayuda de antemano!
Lo que hago en tales situaciones es usar prefetch-related
a_qs = A.objects.all().prefetch_related( models.Prefetch('b_set', # NOTE: no need to filter with OuterRef (it wont work anyway) # Django automatically filter and matches B objects to A queryset=B_queryset, to_attr='b_records' ) ) Ahora a.b_records será una lista que contiene a's objetos b relacionados con a. Dependiendo de cómo filtre su B_queryset esta lista puede estar limitada a solo 1 objeto.