tutorial queryset example cual consultas avanzadas python django database-design

python - queryset - django tutorial



Modelos Django: cómo filtrar el número de objetos ForeignKey (5)

Tengo los modelos A y B , que son así:

class A(models.Model): title = models.CharField(max_length=20) (...) class B(models.Model): date = models.DateTimeField(auto_now_add=True) (...) a = models.ForeignKey(A)

Ahora tengo algunos objetos A y B , y me gustaría obtener una consulta que seleccione todos los objetos A que tengan menos de 2 B apuntando a ellos.

A es algo parecido a un pool, y los usuarios (los B) se unen al grupo. si solo hay 1 o 0 unidos, el grupo no debería mostrarse en absoluto.

¿Es posible con tal diseño de modelo? ¿O debería modificar eso un poco?


¿Qué hay de hacer de esta manera?

queryset = A.objects.filter(b__gt=1).distinct()


La pregunta y la respuesta seleccionada son de 2008 y desde entonces esta funcionalidad se ha integrado en el marco de django. Dado que este es uno de los principales resultados de google para "filtro de django cuenta de clave externa", me gustaría agregar una solución más fácil con una versión reciente de django mediante Aggregation .

from django.db.models import Count cats = A.objects.annotate(num_b=Count(''b'')).filter(num_b__lt=2)

En mi caso, tuve que llevar este concepto un paso más allá. Mi objeto "B" tenía un campo booleano llamado is_available, y solo quería devolver los objetos A que tenían más de 0 objetos B con is_available establecido en True.

A.objects.filter(B__is_available=True).annotate(num_b=Count(''b'')).filter(num_b__gt=0).order_by(''-num_items'')


Recomiendo modificar su diseño para incluir algún campo de estado en A.

El problema es uno de "¿por qué?" ¿Por qué A tiene <2 B y por qué A tiene> = 2 B? ¿Es porque el usuario no ingresó algo? O es porque lo intentaron y su entrada tuvo errores. ¿O es porque la regla <2 no se aplica en este caso?

El uso de la presencia o ausencia de una clave externa limita el significado a - bien presente o ausente. No tienes forma de representar "¿por qué?"

Además, tienes la siguiente opción

[ a for a in A.objects.all() if a.b_set.count() < 2 ]

Esto puede ser costoso porque capta todas las A en lugar de forzar a la base de datos a hacer el trabajo.

Editar: A partir del comentario "requeriría que mirase para usuario unirse / usuario dejando los eventos de grupo".

No "mira" nada, proporciona una API que hace lo que necesita. Ese es el beneficio central del modelo de Django. Aquí hay una forma, con métodos explícitos en la clase A

class A( models.Model ): .... def addB( self, b ): self.b_set.add( b ) self.changeFlags() def removeB( self, b ): self.b_set.remove( b ) self.changeFlags() def changeFlags( self ): if self.b_set.count() < 2: self.show= NotYet else: self.show= ShowNow

También puede definir un Manager especial para esto y reemplazar el Administrador b_set predeterminado por su administrador que cuenta las referencias y actualizaciones A


Suena como un trabajo por extra .

A.objects.extra( select={ ''b_count'': ''SELECT COUNT(*) FROM yourapp_b WHERE yourapp_b.a_id = yourapp_a.id'', }, where=[''b_count < 2''] )

Si el conteo B es algo que a menudo necesita como criterio de filtrado u orden, o necesita mostrarse en vistas de lista, podría considerar la desnormalización agregando un campo b_count a su modelo A y usando señales para actualizarlo cuando se agrega una B o eliminado:

from django.db import connection, transaction from django.db.models.signals import post_delete, post_save def update_b_count(instance, **kwargs): """ Updates the B count for the A related to the given B. """ if not kwargs.get(''created'', True) or kwargs.get(''raw'', False): return cursor = connection.cursor() cursor.execute( ''UPDATE yourapp_a SET b_count = ('' ''SELECT COUNT(*) FROM yourapp_b '' ''WHERE yourapp_b.a_id = yourapp_a.id'' '') '' ''WHERE id = %s'', [instance.a_id]) transaction.commit_unless_managed() post_save.connect(update_b_count, sender=B) post_delete.connect(update_b_count, sender=B)

Otra solución sería administrar un indicador de estado en el objeto A cuando está agregando o eliminando un B relacionado.

B.objects.create(a=some_a) if some_a.hidden and some_a.b_set.count() > 1: A.objects.filter(id=some_a.id).update(hidden=False) ... some_a = b.a some_b.delete() if not some_a.hidden and some_a.b_set.count() < 2: A.objects.filter(id=some_a.id).update(hidden=True)


Supongo que unirse o abandonar el grupo puede no ser tan frecuente como mostrar (mostrar) los grupos. También creo que sería más eficiente para los usuarios unirse / dejar acciones para actualizar el estado de visualización de la agrupación. De esta forma, listar y mostrar las agrupaciones requeriría menos tiempo ya que solo ejecutaría una única consulta para SHOW_STATUS de los objetos de la agrupación.