问题
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