modelo - tipos de campos django
Filtro Django() en el campo del modelo relacionado (1)
Creo que me estoy perdiendo algo muy básico y fundamental sobre cómo se supone que el método Django filter () funciona.
Usando los siguientes modelos:
class Collection(models.Model):
pass
class Item(models.Model):
flag = models.BooleanField()
collection = models.ForeignKey(Collection)
y con los datos suministrados llamando a la función populate () en la parte inferior de la pregunta, intente ejecutar lo siguiente en el shell ./manage.py:
len(Collection.objects.filter(item__flag=True))
Mi expectativa era que esto imprimiera "2", que es el número de Colecciones que tienen al menos un Artículo con bandera = Verdadero. Esta expectativa se basó en la documentación en https://docs.djangoproject.com/en/1.5/topics/db/queries/#lookups-that-span-relationships , que tiene un ejemplo que dice "Este ejemplo recupera todos los objetos de entrada con un Blog cuyo nombre es ''Blog de los Beatles'' ".
Sin embargo, la llamada anterior en realidad imprime "6", que es el número de registros de elementos que tienen marca = Verdadero. Sin embargo, los objetos reales devueltos son objetos Collection. Parece que devuelve el mismo objeto Collection muchas veces, una por cada registro de artículo correspondiente con flag = True. Esto puede ser confirmado por:
queryset = Collection.objects.filter(item__flag=True)
queryset[0] == queryset[1]
que imprime verdadero.
¿Es este el comportamiento correcto? Si es así, ¿cuál es el razonamiento? Si es lo que se espera, la documentación podría interpretarse como estrictamente correcta, pero omite decir que cada objeto se puede devolver varias veces.
Aquí hay un ejemplo relacionado, que parece ser un comportamiento muy sorprendente (o simplemente erróneo). Me sorprendió en un caso en el que un administrador de modelo personalizado agregaba una llamada a exclude () y la persona que llamaba agregaba un filtro ():
from django.db.models import Count
[coll.count for coll in Collection.objects.filter(item__flag=True).annotate(count=Count("item"))]
[coll.count for coll in Collection.objects.exclude(item=None).filter(item__flag=True).annotate(count=Count("item"))]
El primer caso imprime "[2,4]", pero el segundo imprime "[8,16]" !!!
Función de poblado:
def populate():
Collection.objects.all().delete()
collection = Collection()
collection.save()
item = Item(collection=collection, flag=True)
item.save()
item = Item(collection=collection, flag=True)
item.save()
item = Item(collection=collection, flag=False)
item.save()
item = Item(collection=collection, flag=False)
item.save()
collection = Collection()
collection.save()
item = Item(collection=collection, flag=True)
item.save()
item = Item(collection=collection, flag=True)
item.save()
item = Item(collection=collection, flag=True)
item.save()
item = Item(collection=collection, flag=True)
item.save()
collection = Collection()
collection.save()
item = Item(collection=collection, flag=False)
item.save()
item = Item(collection=collection, flag=False)
item.save()
item = Item(collection=collection, flag=False)
item.save()
item = Item(collection=collection, flag=False)
item.save()
Resulta que hay dos partes en esto. Primero está el método distinct (), para el cual el documento dice:
Por defecto, un QuerySet no eliminará las filas duplicadas. En la práctica, esto rara vez es un problema, porque consultas simples como Blog.objects.all () no introducen la posibilidad de filas de resultados duplicados. Sin embargo, si su consulta abarca varias tablas, es posible obtener resultados duplicados cuando se evalúa un QuerySet. Ahí es cuando usarías distinct ().
Las siguientes salidas "2" como se esperaba:
len(Collection.objects.filter(item__flag=True).distinct())
Sin embargo, esto no ayuda con el ejemplo más complejo que di, usando anotar (). Resulta que esta es una instancia de un problema conocido: https://code.djangoproject.com/ticket/10060 .