Vaya a editar para ver más ejemplos de código de la vida real, que no funcionan después de cambiar el orden de consulta
Aquí están mis modelos:
class ModelA(models.Model): field_1a = models.CharField(max_length=32) field_2a = models.CharField(max_length=32) class ModelB(models.Model): field_1b = models.CharField(max_length=32) field_2b = models.CharField(max_length=32)Ahora, crea 2 instancias cada una:
ModelA.objects.create(field_1a="1a1", field_2a="1a2") ModelA.objects.create(field_1a="2a1", field_2a="2a2") ModelB.objects.create(field_1b="1b1", field_2b="1b2") ModelB.objects.create(field_1b="2b1", field_2b="2b2")Si consulto solo un modelo con anotaciones, obtengo algo así:
>>> ModelA.objects.all().annotate(field1=F("field_1a"), field2=F("field_2a")).values("field1", "field2") [{"field1": "1a1", "field2": "1a2"}, {"field1": "2a1", "field2": "2a2"}]Este es el comportamiento correcto. El problema empieza, cuando quiero conseguir la unión de esos dos modelos:
# model A first, with annotate query = ModelA.objects.all().annotate(field1=F("field_1a"), field2=F("field_2a")) # now union with model B, also annotated query = query.union(ModelB.objects.all().annotate(field1=F("field_1b"), field2=F("field_2b"))) # get only field1 and field2 query = query.values("field1", "field2") # the results are skewed: assert list(query) == [ {"field1": 1, "field2": "1a1"}, {"field1": 1, "field2": "1b1"}, {"field1": 2, "field2": "2a1"}, {"field1": 2, "field2": "2b1"}, ] La aserción pasa correctamente, lo que significa que los resultados son incorrectos. Parece que los values() no coincidieron con el nombre de la variable, simplemente iteraron sobre el objeto como en una tupla. El valor de field1 es en realidad el ID del objeto y field2 es field1 .
Esto es bastante fácil de arreglar en modelos tan simples, pero mis modelos reales son bastante complejos y tienen una cantidad diferente de campos. ¿Cómo los uno correctamente?
A continuación, puede encontrar un ejemplo extendido que falla independientemente del orden de union() y values() : los modelos son un poco más grandes ahora, y parece que la cuenta de diferentes campos de alguna manera confunde a Django:
# models class ModelA(models.Model): field_1a = models.CharField(max_length=32) field_1aa = models.CharField(max_length=32, null=True) field_1aaa = models.CharField(max_length=32, null=True) field_2a = models.CharField(max_length=32) extra_a = models.CharField(max_length=32) class ModelB(models.Model): extra = models.CharField(max_length=32) field_1b = models.CharField(max_length=32) field_2b = models.CharField(max_length=32) # test ModelA.objects.create(field_1a="1a1", field_2a="1a2", extra_a="1extra") ModelA.objects.create(field_1a="2a1", field_2a="2a2", extra_a="2extra") ModelB.objects.create(field_1b="1b1", field_2b="1b2", extra="3extra") ModelB.objects.create(field_1b="2b1", field_2b="2b2", extra="4extra") values = ("field1", "field2", "extra") query = ( ModelA.objects.all() .annotate( field1=F("field_1a"), field2=F("field_2a"), extra=F("extra_a") ) .values(*values) ) query = query.union( ModelB.objects.all() .annotate(field1=F("field_1b"), field2=F("field_2b")) .values(*values) ) # outcome assert list(query) == [ {"field1": "1a1", "field2": "1a2", "extra": "1extra"}, {"field1": "2a1", "field2": "2a2", "extra": "2extra"}, {"field1": "3extra", "field2": "1b1", "extra": "1b2"}, {"field1": "4extra", "field2": "2b1", "extra": "2b2"}, ]Busqué en los documentos y debo admitir que no entendí completamente por qué su enfoque no funcionó (según tengo entendido, debería). Lo que creo es que aplicar la union a conjuntos de consultas con diferentes nombres de campo parece estar produciendo efectos extraños.
De todos modos, aplicar valores antes de hacer la unión parece producir resultados correctos:
query = ModelA.objects.all().annotate(field1=F("field_1a"), field2=F("field_2a")).values('field1', 'field2') query = query.union(ModelB.objects.all().annotate(field1=F("field_1b"), field2=F("field_2b")).values('field1', 'field2'))Lo que da como resultado este conjunto de consultas
[ {'field1': '1a1', 'field2': '1a2'}, {'field1': '1b1', 'field2': '1b2'}, {'field1': '2a1', 'field2': '2a2'}, {'field1': '2b1', 'field2': '2b2'} ]