Count number of records by date in Django

PythonDjangoDjango ModelsDjango Queryset

Python Problem Overview


I have a model similar to the following:

class Review(models.Model):
    venue = models.ForeignKey(Venue, db_index=True)
    review = models.TextField()  
    datetime_created = models.DateTimeField(default=datetime.now)

I'd like to query the database to get the total number of reviews for a venue grouped by day. The MySQL query would be:

SELECT DATE(datetime_created), count(id) 
FROM REVIEW 
WHERE venue_id = 2
GROUP BY DATE(datetime_created);

What is the best way to accomplish this in Django? I could just use

Review.objects.filter(venue__pk=2)

and parse the results in the view, but that doesn't seem right to me.

Python Solutions


Solution 1 - Python

This should work (using the same MySQL specific function you used):

Review.objects.filter(venue__pk=2)
    .extra({'date_created' : "date(datetime_created)"})
    .values('date_created')
    .annotate(created_count=Count('id'))

Solution 2 - Python

Now that Extra() is being depreciated a more appropriate answer would use Trunc such as this accepted answer

Now the OP's question would be answered as follows

from django.db.models.functions import TruncDay

Review.objects.all()
    .annotate(date=TruncDay('datetime_created'))
    .values("date")
    .annotate(created_count=Count('id'))
    .order_by("-date")

Solution 3 - Python

Just for completeness, since extra() is aimed for deprecation, one could use this approach:

from django.db.models.expressions import DateTime

Review.objects.all().\
    annotate(month=DateTime("timestamp", "month", pytz.timezone("Etc/UTC"))).\
    values("month").\
    annotate(created_count=Count('id')).\
    order_by("-month")

It worked for me in django 1.8, both in sqlite and MySql databases.

Solution 4 - Python

If you were storing a date field, you could use this:

from django.db.models import Count

Review.objects.filter(venue__pk = 2)
    .values('date').annotate(event_count = Count('id'))

Because you're storing datetime, it's a little more complicated, but this should offer a good starting point. Check out the aggregation docs here.

Solution 5 - Python

Also you can define custom function:

from django.db.models.expressions import Func

# create custom sql function
class ExtractDateFunction(Func):
    function = "DATE" # thats the name of function, the way it mapped to sql

# pass this function to annotate
Review.objects.filter(venue__pk=2)
      .annotate(date_created=ExtractDateFunction("datetime_created"))
      .values('date_created')
      .annotate(created_count=Count('id'))

Just make sure that your DB engine supports DATE function

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondozaView Question on Stackoverflow
Solution 1 - Pythonara818View Answer on Stackoverflow
Solution 2 - PythonAnthony Manning-FranklinView Answer on Stackoverflow
Solution 3 - PythonavikamView Answer on Stackoverflow
Solution 4 - PythonZachView Answer on Stackoverflow
Solution 5 - PythonMadisonTrashView Answer on Stackoverflow