Django Count of Items in a Field

痞子三分冷 提交于 2020-03-19 05:05:25

问题


models.py

class Event(models.Model):
    name = models.CharField(max_length=20, unique=True)
    distance = models.IntegerField()
    date = models.DateField()

class Category(models.Model):
    name = models.CharField(max_length=20, unique=True)
    description = models.CharField(max_length=20, unique=True)
    isnew = models.BooleanField(default=False)

class Result(models.Model):
    event = models.ForeignKey(Event)
    category = models.ForeignKey(Category)
    score = models.IntegerField()

I want to do a query to return a count of each unique Category in the Result table, for a given Event.

What I'm doing now is something like:

results = Result.objects.filter(event=myevent)
categorycountdict = {}
for r in results:
    if r.category in categorycountdict:
        categorycountdict[r.category] += 1
    else:
        categorycountdict[r.category] = 1

Is there a better way, perhaps by query instead of python.


回答1:


You can use annotate() with values(). This approach is shown in the docs for values(). To get the count for each category name, you could do:

from django.db.models import Count

categories = Result.objects.filter(
    event=myevent,
).order_by('category').values(
    'category__name'
).annotate(count=Count('category__name'))

This will return a list of dictionaries with keys category__name and count, for example:

[{'count': 3, 'category__name': u'category1'}, {'count': 1, 'category__name': u'category2'}]

You could convert this to a single dictionary by using a dictionary comprehension:

counts_by_category = {d['category__name']: d['count'] for f in categories}



回答2:


There is collections.Counter in python standard library:

results = Result.objects.filter(event=myevent).select_related('category')
c = Counter(r.category for r in results)

Now c is a dict-like object where keys are Category instances, and values are counts.

This option is not suitable for large datasets though, since it doesn't use database features. So if you have a lot of data, approaches that use values, annotate and Count are more suitable.

select_related is used here in order to eliminate database hit for every result. Basically it makes 1 query with join and generates python objects for categories.



It turns out, that ManyToManyField keeps track of unique records only, so below answer is incorrect.

Your Event and Category models expose many-to-many connection through Result model.

You can express it using through in Django (note categories ManyToManyField):

class Event(models.Model):
    name = models.CharField(max_length=20, unique=True)
    distance = models.IntegerField()
    date = models.DateField()
    categories = models.ManyToManyField(Category, through='Result', related_name='events')

In your code, for given event you could query like this:

event.categories.count()

Taking Alasdair note into account:

event.categories.values('pk').annotate(count=Count('pk'))



回答3:


Use annotate:

from django.db.models import Count

Results.objects.filter(event=some_event).annotate(Count('category'), distinct=True)


来源:https://stackoverflow.com/questions/39513627/django-count-of-items-in-a-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!