Filtering django model using date - django

I need to filter some data from models using the date. I see some posts that speaks about ranges but I just want to see for example the rows of my table from the 22/04/2020, in other words, just one day.
Reading the documentation, I understood that I have to do the following,
import datetime
prueba = DevData.objects.order_by('-data_timestamp').filter(data_timestamp=datetime.date(2020, 4, 22))
prueba = loads(serializers.serialize('json', prueba))
for p in prueba:
print(p)
But appears the following warning:
RuntimeWarning: DateTimeField DevData.data_timestamp received a naive datetime (2020-04-22 00:00:00) while time zone support is active.
And the list appears empty. I think the problem is that is just filtering using 2020-04-22 00:00:00 and not all the day. How can I fix this?
Between two dates is working but just one day I don't know how to do it.
import datetime
start_date = datetime.date(2020, 4, 22)
end_date = datetime.date(2020, 4, 23)
prueba = DevData.objects.order_by('-data_timestamp').filter(data_timestamp__range= (start_date,end_date))
PD: I have info rows in this day.
Thank you very much.

You can make use of the __date lookup [Django-doc] to filter on the date of the timestamp:
import datetime
prueba = DevData.objects.order_by(
'-data_timestamp'
).filter(
data_timestamp__date=datetime.date(2020, 4, 22)
)

Related

Django ORM: timedelta difference in days between 2 dates

I have a database table that represents expenses associated with a given product.
These expenses, given that they're daily, have a from_date (the date in which they started) and to_date (the date in which they ended). to_date can be null, as these expenses might still be going.
Given 2 Python datetimes, start_date and end_date, I need to produce in the ORM the total spent in the period for my_product.
>>> start_date
datetime.datetime(2021, 8, 20, 0, 0)
>>> end_date
datetime.datetime(2021, 9, 21, 0, 0)
In this case, the expected output should be:
(-104 * (days between 08/20 and 08/25)) + (-113 * (days between 08/26 and 09/21)
This is what I've got so far:
(
my_product.income_streams
.values("product")
.filter(type=IncomeStream.Types.DAILY_EXPENSE)
.filter(add_to_commission_basis=True)
.annotate(period_expenses=Case(
When(Q(from_date__lte=start_date) & Q(to_date__lte=end_date),
then=ExpressionWrapper( start_date - F('to_date'), output_field=IntegerField()))
), # Other When cases...
)
) # Sum all period_expenses results and you've got the solution
And this is what's giving me problems:
then=ExpressionWrapper( start_date - F('to_date'), output_field=IntegerField())
This expression always returns 0 (please note this is why I'm not even attempting to multiply by value: that'd be the next step).
Apparently start_date - F('to_date') is not the same as "give me the difference in days between these 2 dates".
You'd acomplish this in Python with timedelta. What's the equivalent in the ORM?
I've tried with ExtractDay:
then=ExpressionWrapper( ExtractDay(start_date - F('to_date'))
But I get: django.db.utils.OperationalError: user-defined function raised exception
And also tried with DurationField:
then=ExpressionWrapper(start_date - F('to_date'), output_field=DurationField())
But that also returns zero: datetime.timedelta(0)
Casting start_date into a DateTimeField solves the problem, and casting the difference into a DurationField is the next step.
So:
Cast(Cast(start_date, output_field=DateTimeField()) - F('to_date'), output_field=DurationField())
This will work fine on any databse backend, but in order to get the difference in days, you need to wrap it in ExtractDay, which will throw ValueError: Extract requires native DurationField database support. if you use SQLite.
If you're tied to SQLite and cannot use ExtractDay, you can use microseconds, and convert them manually to days by dividing by 86400000000
duration_in_microseconds=ExpressionWrapper(F('to_date') - (Cast(start_date, output_field=DateTimeField())), output_field=IntegerField())
Then
.annotate(duration_in_days=ExpressionWrapper(F('period_duration_microseconds') / 86400000000, output_field=DecimalField())

Django: How to get the list of month within a daterange in django query

Suppose I have query:
ExampleModel.objects.filter(some_datetime_field__gte=start, some_datetime_field__lte=end)
How do I get the list of all months present within "start" and "end" in the above mentioned query.
For example:
IF
start= 1/10/2018 and end=10/1/2019
Then the output will be:
OCTOBER
NOVEMBER
DECEMBER
JANUARY
Anyone any idea how to perform this?
Thank you in advance
You can extract months and then get their names
from django.db.models.functions import ExtractMonth
months = (
ExampleModel.objects
.filter(some_datetime_field__gte=start, some_datetime_field__lte=end)
.annotate(month=ExtractMonth('some_datetime_field'))
.values_list('month', flat=True)
.distinct()
)
At the end of this code you'll have a list of months(numbers). for example
[1, 3, 6, 8]
And you can get their names using calendar
import calendar
[calendar.month_name[month] for month in months]
You can use annotation and Query Expressions.
import calendar
from django.db.models import Case, When, Value, CharField
conditions = []
for i in range(1, 13):
month_name = calendar.month_name[i]
conditions.append(When(some_datetime_field__month=i, then=Value(month_name)))
# conditions will be like below
# [
# When(some_datetime_field__month=1, then=Value('January')),
# When(some_datetime_field__month=2, then=Value('February')),
# ...
# ]
ExampleModel.objects.annotate(
month_name=Case(*conditions, default=Value(""), output_field=CharField())
).order_by("month_name").values_list("month_name", flat=True).distinct()
# Result will be like
# <ExampleModelQuerySet ['January', 'September']>

Django __date not working with date() or string

I have the following model field:
inbound_date = models.DateTimeField()
I want to filter on date, and not care about the time.
I have inserted two test objects which return the following values for inbound_date:
2018-11-14 00:00:00+00:00
2018-11-15 08:37:09+00:00
But all my attempts at retrieving the objects fail:
AppInbound.objects.filter(inbound_date__date=datetime.date(2018, 11, 15))
Or
AppInbound.objects.filter(inbound_date__date='2018-11-15')
But all seem to return an empty QuerySet.
The only one I did manage to get working was:
AppInbound.objects.filter(inbound_date__startswith='2018-11-15')
Which returns the the last objects, with datetime of 2018-11-15 08:37:09+00:00.
Can someone explain what is going wrong here?
It is an MySQL database, and we have TZ enabled.
You can try like this:
AppInbound.objects.filter(inbound_date__lt=datetime.date(2018, 11, 15), inbound_date__gte=datetime.date(2018, 11, 14)) # will return objects for 14th November.
Or you can check this answer on how to filter on specific dates.
If you are only concerned with using date then DateField should be used here:
inbound_date = models.DateField()
Then you should be able to filter with date object(datetime.date).

How to compare two Datetime field in Django

I have used datetime.datetime.now() for storing datefield in my model which is saved as 2016-06-27 15:21:17.248951+05:30. Now I want to compare the datefield with the datetime value getting from the frontend, like Thu May 26 2016 00:00:00 GMT 0530 (IST). How should Django query the model to compare both datefields?
# models.py
datefield = models.DateTimeField(blank=True,null=True)
I have tried converting datefield getting from frontend by using split() and remove() function of Python to create it in format as 2016-06-27 13:25:35.
But still no solution and getting Null even I am comparing same date value like this (2016-06-27 13:25:35) value with this (2016-06-27 12:36:34.898593+00) value, as date in both values are same.
I have checked it using simple Django query as follows:
company_details.objects.filter(datefield=datefield).only('par1','par2',...)
Once you have converted the date from the front end into a ISO format like 2016-06-27 13:25:35, you can use it to query the model with one of these
Model.objects.filter(date_created__startswith=today)
Model.objects.filter(date_created__contains=today)
It works too if you are only looking to filter for a certain date like 2016-06-27.
Model.objects.filter(date_created__startswith=date(2016, 6, 27))
To parse the date string you are getting from the frontend, there are two options.
If your date string is well-formatted, you can simply parse it with for example datestring.strftime('%Y-%m-%d %H:%M:%S') to get a datetime() object.
If your date string is unreliable and may be formatted in different ways that you can't predict, I would recommend to using DateUtil. It comes with a parser that will try to convert any string into a datetime, for example
>>> from dateutil.parser import parse
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
Compare the date and time like this. Just give the variable like this '%Y-%m-%d %H:%M:%S' as much you want.
In [1]: from datetime import datetime
In [2]: past = datetime.now()
In [3]: present = datetime.now()
In [4]: present.strftime('%Y-%m-%d %H:%M:%S') == past.strftime('%Y-%m-%d %H:%M:%S')
Out[17]: False
In [5]: present.strftime('%Y-%m-%d %H:%M') == past.strftime('%Y-%m-%d %H:%M')
Out[2]: True
If you want to compare only the date use like this.
if present.date() == past.date():
#Do your stuff

How to put timedelta in django model?

With inspectdb I was able to get a "interval" field from postgres into django. In Django, it was a TextField. The object that I retrieved was indeed a timedelta object!
Now I want to put this timedelta object in a new model. What's the best way to do this? Because putting a timedelta in a TextField results in the str version of the object...
Since Django 1.8 you can use DurationField.
You can trivially normalize a timedelta to a single floating-point number in days or seconds.
Here's the "Normalize to Days" version.
float(timedelta.days) + float(timedelta.seconds) / float(86400)
You can trivially turn a floating-point number into a timedelta.
>>> datetime.timedelta(2.5)
datetime.timedelta(2, 43200)
So, store your timedelta as a float.
Here's the "Normalize to Seconds" version.
timedelta.days*86400+timedelta.seconds
Here's the reverse (using seconds)
datetime.timedelta( someSeconds/86400 )
First, define your model:
class TimeModel(models.Model):
time = models.FloatField()
To store a timedelta object:
# td is a timedelta object
TimeModel.objects.create(time=td.total_seconds())
To get the timedelta object out of the database:
# Assume the previously created TimeModel object has an id of 1
td = timedelta(seconds=TimeModel.objects.get(id=1).time)
Note: I'm using Python 2.7 for this example.
https://bitbucket.org/schinckel/django-timedelta-field/src
There is a ticket which dates back to July 2006 relating to this:
https://code.djangoproject.com/ticket/2443
Several patches were written but the one that was turned in to a project:
https://github.com/johnpaulett/django-durationfield
Compared to all the other answers here this project is mature and would have been merged to core except that its inclusion is currently considered to be "bloaty".
Personally, I've just tried a bunch of solutions and this is the one that works beautifully.
from django.db import models
from durationfield.db.models.fields.duration import DurationField
class Event(models.Model):
start = models.DateTimeField()
duration = DurationField()
#property
def finish(self):
return self.start + self.duration
Result:
$ evt = Event.objects.create(start=datetime.datetime.now(), duration='1 week')
$ evt.finish
Out[]: datetime.datetime(2013, 6, 13, 5, 29, 29, 404753)
And in admin:
Change event
Duration: 7 days, 0:00:00
For PostgreSQL, use django-pgsql-interval-field here: http://code.google.com/p/django-pgsql-interval-field/
Putting this out there cause it might be another way to solve this problem.
first install this library: https://pypi.python.org/pypi/django-timedeltafield
Then:
import timedelta
class ModelWithTimeDelta(models.Model):
timedeltafield = timedelta.fields.TimedeltaField()
within the admin you will be asked to enter data into the field with the following format: 3 days, 4 hours, 2 minutes
There is a workaround explained here. If you're using Postgresql, then multiplying the result of F expression with timedelta solves the problem. For example if you have a start_time and a duration in minutes, you can calculate the end_time like this:
YourModel.objects.annotate(
end_time=ExpressionWrapper(F('start_time') + timedelta(minutes=1) * F('duration'), output_field=DateTimeField())
)