简体   繁体   中英

How to count objects from database filtered by a choice field in django?

I have Porumbei model and it has gender field. In template I must render the number of Porumbei of each gender. For example: Male= x Porumbei, Female= y Porumbei and Unknown = z Porumbei.

My model

class Porumbei(models.Model):
    SEXE = [
        ('Mascul', 'Mascul'),
        ('Femelă', 'Femelă'),
        ('Pui', 'Pui')
    ]
    ...
    gender = models.CharField(max_length=6, choices=SEXE, db_index=True)
    ...

For Total I have:

total = Porumbei.objects.count()

Below is what I have now:

@staticmethod
def masculi(crescator=None):
    """ Numarul de masculi din sistem """
    mascul = Porumbei.objects.filter(gender="Mascul", is_active=True, crescator=crescator)
    return len(mascul)

@staticmethod
def femele(crescator=None):
    """ Numarul de femele din sistem """
    femela = Porumbei.objects.filter(gender="Femelă", is_active=True, crescator=crescator)
    return len(femela)

@staticmethod
def pui(crescator=None):
    """ Numarul de pui din sistem """
    pui = Porumbei.objects.filter(gender="Pui", is_active=True, crescator=crescator)
    return len(pui)

These functions gets me what I want but from my point of view, are not well optimized. This will end up in three similar queries as I saw in django debug toolbar. My question is, how can I get the number of each gender in only one query. Is it possible with another method or this function can be optimized to do a single query in database? I must mention that with the number of each gender, arithmetic operations must be performed. For example, how much of the total is each gender. Thanks in advance!

You can use values() , and annotate() to run a GROUP BY on gender field, and then get count.

from django.db.models import Count

gender_count = Porumbei.objects.values('gender').annotate(count=Count('gender')).order_by()

order_by() is an optional clause here, but it should be added when default ordering is defined for the Model . From docs :

Fields that are mentioned in the order_by() part of a queryset (or which are used in the default ordering on a model) are used when selecting the output data, even if they are not otherwise specified in the values() call. These extra fields are used to group “like” results together and they can make otherwise identical result rows appear to be separate. This shows up, particularly, when counting things.

Adding order_by() in the end clears any default ordering.

How can I render this into template?

gender_count dictionary will contain gender values as key, and corresponding count as value.

To render, gender_count in template you can iterate over the dict.

{% for gender, count in gender_count.items %} 
    <p>{{gender}}: {{count}}</p>
{% endfor %}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM