Obtener un recuento de objetos en un conjunto de consultas en Django

3 minutos de lectura

avatar de usuario de thesteve
steve

¿Cómo puedo agregar un campo para el recuento de objetos en una base de datos? Tengo los siguientes modelos:

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

class Contest(models.Model);
    name = models.CharField()

class Votes(models.Model):
    user = models.ForeignKey(User)
    item = models.ForeignKey(Item)
    contest = models.ForeignKey(Contest)
    comment = models.TextField()

Para encontrar los votos para contestA estoy usando la siguiente consulta en mi opinión

current_vote = Item.objects.filter(votes__contest=contestA)

Esto devuelve un conjunto de consultas con todos los votos individualmente, pero quiero obtener el recuento de votos para cada elemento, ¿alguien sabe cómo puedo hacerlo? Gracias

Para obtener el número de votos para un elemento específico, usaría:

vote_count = Item.objects.filter(votes__contest=contestA).count()

Si quisiera un desglose de la distribución de votos en una contienda en particular, haría algo como lo siguiente:

contest = Contest.objects.get(pk=contest_id)
votes   = contest.votes_set.select_related()

vote_counts = {}

for vote in votes:
  if not vote_counts.has_key(vote.item.id):
    vote_counts[vote.item.id] = {
      'item': vote.item,
      'count': 0
    }

  vote_counts[vote.item.id]['count'] += 1

Esto creará un diccionario que asigna elementos a la cantidad de votos. No es la única forma de hacer esto, pero es bastante ligero en los resultados de la base de datos, por lo que se ejecutará con bastante rapidez.

  • para cualquier persona interesada – has_key fue eliminado en Python 3: puede actualizarlo para usar el in operador en su lugar. Ejemplo:. if vote.item.id not in vote_counts: vote_counts[vote.item.id] = { 'item': vote.item, 'count': 0 }

    – RealScatman

    6 de mayo a las 15:43


avatar de usuario de salomonvh
salomonvh

Otra forma de hacerlo sería usando Agregación. Debería poder lograr un resultado similar utilizando una sola consulta. Como esto:

from django.db.models import Count

Item.objects.values("contest").annotate(Count("id"))

No probé esta consulta específica, pero debería generar un recuento de los elementos para cada valor en los concursos como un diccionario.

  • Esto todavía es confuso para mí. Si anota en un conjunto de consultas, está contando la cantidad de ID que pertenecen a cada objeto, que obviamente es 1. Entonces, ¿cómo es eso útil?

    – AlxVallejo

    14 de junio de 2018 a las 17:08

  • Si, si estuvieras contando contest en el modelo Contest ese sería el caso. Sin embargo, en este caso estamos agregando en Item que tienen concursos únicos.

    – salomonvh

    18 de junio de 2018 a las 8:25

  • ¿Qué sucede si el recuento de dos objetos es el mismo y luego tenemos que clasificarlos en función de alguna otra condición? Entonces, ¿cómo hacer eso?

    – Tarún

    6 sep 2018 a las 20:24

  • @Tarun puede agregar valores en la anotación, solo asegúrese de no intentar agregar elementos que se agreguen durante la anotación. Dado que devuelve un conjunto de consultas, se siguen aplicando todas las funciones de ordenación habituales. .order_by('some_value').

    – salomonvh

    7 sep 2018 a las 17:16

Use un nombre relacionado para contar los votos para un concurso específico

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

class Contest(models.Model);
    name = models.CharField()

class Votes(models.Model):
    user = models.ForeignKey(User)
    item = models.ForeignKey(Item)
    contest = models.ForeignKey(Contest, related_name="contest_votes")
    comment = models.TextField()

>>> comments = Contest.objects.get(id=contest_id).contest_votes.count()

Puedes usar largo() Llegar el conteo de votos de contestA:

current_vote = len(Item.objects.filter(votes__contest=contestA))

¿Ha sido útil esta solución?