Count number of records by date in Django - python

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.

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'))

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")

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.

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.

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

Related

use __gte for string date django

Date is in string format in database
class A(models.Model):
date = models.CharField(max_length=50, blank=True, null=True)
Yah i know it should be date type.
but now we have many records and we will soon change it to date type.
For current status i want get the objects greater than particular date.
So how can i use date__gte
example
objs = A.objects.filter(date__gte=datetime.now())
Is there any way to achieve this without converting date to datetime field.
I am not sure if that works. What you can try is a custom manager. So you can create a manager for your model, add something like date_gte and then convert the string to a datetime. Then you can user those operators as usual. That's a quick fix for now, but the best solution is to use a DateTimeField, which you want to do as far as I understood.
Example Manager:
from django.db import models
class MyManager(models.Manager):
def date_gte(self, date=datetime.now()):
items = []
for obj in self.all():
if datetime(obj.date) < date:
items.append(obj)
return items
Then you could call it like MyModel.objects.date_gte(date=datetime.now()).
Note: This is an expensive query and you may need to convert the simple list into QuerySet object. I haven't tested it, so this example should only help you get started.
There is no way to do this without a conversion (either in Django or the database during the query) to a proper DateTime type. You're trying to compare a datetime.datetime to a str. That won't work in normal Python and it won't work here.
What kind of string? if it is formatted to %Y%m%d, you can use .extra() method to do the query.
A.objects.extra(where=['date >= date_of_today'])

Django - Filter objects older than X days

I need to filter objects older than X number of days. I realize this question exists here: django filter older than day(s)?
However, I not trying to do exactly that, since the number of days, in my case lives inside the model:
class Post(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=500)
createdAt = models.DateTimeField(default=datetime.now, blank=True)
plan = models.ForeignKey(Plan) # This model has the number of days
This is the query I have so far:
EDIT: I changed the days.plan part for the_post.plan.days Meaning that the number of days that I'm using to compare is in each post's plan field.
Post.objects.filter(createdAt__lte=datetime.now() - timedelta(days=the_post.plan.days))
Note the plan.days part of the query. How can I reference the_post.plan.days for this query? Is it possible?
With a small tweak in your Plan model, it is indeed possible to do what you want.
First of all, you'll need to change your Plan days field, which is probably an IntegerField, to DurationField.
Now the catch is that we have to use ExpressionWrapper to achieve the exact same result inside Postgres as the result you'd achieve in Python if you were to get the plan in a separate query.
Finally, your query should be something like:
from django.db.models import F, ExpressionWrapper, DateTimeField
from django.utils import timezone
Post.objects.annotate(target_date=ExpressionWrapper(timezone.now() - F('plan__days'), output_field=DateTimeField())).filter(createdAt__lte=F('target_date'))
Assuming Postgres database:
table_post = Post._meta.db_table
table_plan = Plan._meta.db_table
old_posts = Post.objects.select_related('plan')\
.extra(where=["%s.created_at <= NOW() - INTERVAL '1 day' * %s.days"
% (table_post, table_plan)])
For me you must first grab the plan object.
plan = Plan.objects.filter(...)
and then reference the days
Post.objects.filter(createdAt__lte=datetime.now() - timedelta(days=plan.days))

DJANGO: Mass update field by adding a prefix

I wish to update a Django model field of objects in a queryset. To be specific, I want to add a prefix on one of the fields i.e. if an object has a name like 'Wilson', I want to prefix it with 'OLD', then it will become 'OLDWilson'.
I can think of the following using loops:
my_objs = MyObjects.objects.filter(name='some_name') # This has over 40000 records
for obj in my_objs:
obj.name = 'OLD{0}'.format(obj.name)
obj.save()
I was hoping of a more elegant way to take advantage of the UPDATE method as specified here: Django Mass Update
Something like the following:
MyObjects.objects.filter(name='some_name').update(name='OLD+name_here')
Any pointers on how I can achieve this?
Try something like this :
from django.db.models import F
Myobj = MyObjects.objects.filter(name='some_name')
Myobj.update(name='OLD'+ F('name'))
I know this question is almost 5 years old. But, with hopes that this helps the answer be more visible - here goes.
Use Concat from django.db.models.functions like so:
from django.db.models.functions import Concat
from django.db.models import Value
MyObjects.objects.filter(name='some_name').update(name=Concat(Value'OLD', 'name'))
That last line returns the number of rows affected by the update.
This actually works in the latest version of Django as of May 2022
from django.db.models import Value
from django.db.models.functions import Concat
query = MyObjects.objects.filter(name='some_name')
query.update(name=Concat(Value("OLD"), "name"))

How do I get each distinct 'year' value out of a Django model with a datetime field?

I can successfully filter by a given year in my Django model, but I'm having trouble finding a way to list valid years so a user can access them.
I have a django model with a defined 'datetime' field, oh-so-originally named 'date'. In my templates, I can successfully access the 'bar.date.date.year' field, so I know it exists, but when I try the following function...
blog_years=[]
for entry in blog_entries:
if entry.date.date.year not in blog_years:
blog_years.append(entry.date.date.year)
I'm told that "'builtin_function_or_method' object has no attribute 'year'"
I can only assume I"m tripping over some aspect of Python I'm not familiar with, but I can't figure out what it is. I'm quite certain it has to be syntactical, but past that...
Django has an elegant and efficient way of doing this. You can check from their docs https://docs.djangoproject.com/en/3.0/ref/models/querysets/#dates
But to go over it
Entry.objects.dates('pub_date', 'year')
This will bring out distinct year values in the query.
if you are using postgres, you can do
BlogEntry.objects.extra(select={"year": "EXTRACT(YEAR FROM date)"}).distinct().values_list("year", flat=True)
A Python set does not allow duplicates, so if you wanted a list of distinct years:
blog_years = list(set([entry.date.year for entry in blog_entries]))
Or, you could use distinct():
blog_years = blog_entries.distinct(entry__date__year).values(entry__date__year)
Of course, adjust the above based on your model.
The first .date accesses a datetime object.
The second .date is accessing a method on datetime objects that returns a date object but not calling it (this step is unneccessary).
The last part (the way you wrote it) is trying to access the year attribute of the date method, instead of accessing the year attribute of the result of the date method call.
Correcting the code to see the difference, it would look like this...
blog_years=[]
for entry in blog_entries:
if entry.date.date().year not in blog_years:
blog_years.append(entry.date.date().year)
But what you should do is more like this...
blog_years=[]
for entry in blog_entries:
if entry.date.year not in blog_years:
blog_years.append(entry.date.year)
since datetime objects have the date attribute as well.
date() is a method of datetime, use
blog_years=[]
for entry in blog_entries:
if entry.date.date().year not in blog_years:
blog_years.append(entry.date.date().year)
from django 1.10 it has become very simple
from django.db.models.functions import ExtractYear
blog_years= blog_entries.annotate(
year=ExtractYear('created_on')
).values_list('year', flat=True)
blog_years = sorted(set(blog_years), reverse=True)
It might not be what you exactly expects (it can return years without blog posts):
from datetime import date
from django.db.models import Min
def blog_years():
current_year = date.today().year
queryset = Entry.objects.annotate(Min('date')).order_by('date')
if queryset:
oldest_year = queryset[0].date.date().year
else:
oldest_year = current_year
return range(oldest_year, current_year + 1)
ModelName.objects.dates('column_name', 'year')

Django - SQL Query - Timestamp

Can anyone turn me to a tutorial, code or some kind of resource that will help me out with the following problem.
I have a table in a mySQL database. It contains an ID, Timestamp, another ID and a value. I'm passing it the 'main' ID which can uniquely identify a piece of data. However, I want to do a time search on this piece of data(therefore using the timestamp field). Therefore what would be ideal is to say: between the hours of 12 and 1, show me all the values logged for ID = 1987.
How would I go about querying this in Django? I know in mySQL it'd be something like less than/greater than etc... but how would I go about doing this in Django? i've been using Object.Filter for most of database handling so far. Finally, I'd like to stress that I'm new to Django and I'm genuinely stumped!
If the table in question maps to a Django model MyModel, e.g.
class MyModel(models.Model):
...
primaryid = ...
timestamp = ...
secondaryid = ...
valuefield = ...
then you can use
MyModel.objects.filter(
primaryid=1987
).exclude(
timestamp__lt=<min_timestamp>
).exclude(
timestamp__gt=<max_timestamp>
).values_list('valuefield', flat=True)
This selects entries with the primaryid 1987, with timestamp values between <min_timestamp> and <max_timestamp>, and returns the corresponding values in a list.
Update: Corrected bug in query (filter -> exclude).
I don't think Vinay Sajip's answer is correct. The closest correct variant based on his code is:
MyModel.objects.filter(
primaryid=1987
).exclude(
timestamp__lt=min_timestamp
).exclude(
timestamp__gt=max_timestamp
).values_list('valuefield', flat=True)
That's "exclude the ones less than the minimum timestamp and exclude the ones greater than the maximum timestamp." Alternatively, you can do this:
MyModel.objects.filter(
primaryid=1987
).filter(
timestamp__gte=min_timestamp
).exclude(
timestamp__gte=max_timestamp
).values_list('valuefield', flat=True)
exclude() and filter() are opposites: exclude() omits the identified rows and filter() includes them. You can use a combination of them to include/exclude whichever you prefer. In your case, you want to exclude() those below your minimum time stamp and to exclude() those above your maximum time stamp.
Here is the documentation on chaining QuerySet filters.

Categories

Resources