Handling dates over request.GET - django

I am sending two date values over request.GET for filtering a querySet:
if 'from_date' in request.GET:
from_date = request.GET['from_date']
if 'to_date' in request.GET:
to_date = request.GET['to_date']
else:
to_date = datetime.date.today()
calls_queryset = calls_queryset.filter(contact_date__range=(from_date, to_date))
The filter__range breaks though. It seems it doesn't like the date format I am sending over.
?from_date=08/08/2012&to_date=08/29/2012
I think I have to cast them to be a date before placing them in range, is this correct? What is the most efficient way to do this?
Many Thanks

The DateField supports converting from 'YYYY-mm-dd' formats to python datetime.date:
>>> from django.db import models
>>> models.DateField().to_python('2012-08-22')
datetime.date(2012, 8, 22)
Hence the range lookup accepts strings as parameters:
>>> from django.contrib.auth.models import User
>>> print User.objects.filter(last_login__range=('1970-01-01', '2012-12-31')).values('pk').query
SELECT "auth_user"."id" FROM "auth_user" WHERE "auth_user"."last_login"
BETWEEN 1969-12-31 16:00:00 and 2012-12-30 16:00:00
note that last_login is a DateTimeField which tries to convert string value to datetime.datetime (and I'm in +8 timezone, hence default 00:00:00 becomes 16:00:00 one day before)
On client side, ref How do I output an ISO 8601 formatted string in JavaScript? to generate date string in the format of 'YYYY-mm-dd'.

don't use slashs in url... replace with dots or use yyymmdd format.

Slashes is a no no, I'd convert to UNIX timestamp for convenience
Then to convert it to a python readable format you can do something like:
import time
time.ctime(int(retrieved_timestamp))

Related

Filter data for a given day (timezone aware datetime) from postgres directly

I want to filter all data for the following day (which is timezone aware).
Assumptions:
server data is in a specific timezone
client query is coming from different timezone
My current approach is:
date = received_date_timezone_aware # any time of that day
lower = date.replace(hour=0,minute=0,second=0)
upper = lower + datetime.timedelta(day=1)
data = Model.objects.filter(date__gte=lower, date__lt=upper)
Question
Is there a direct solution to this using django orm or raw query?
Note: I wanted to know if there is a better way which can save me a few lines of code of manipulating the datetime myself
No, there isn't. However what you're doing isn't the best way to do it either as using replace can result in some oddities with day lights savings. I'd recommend using lower = received_date_timezone_aware.date().
This assumes Model.date is a DateField. If it's a datetime, then do this:
from datetime import time, datetime
def min_datetime(date: datetime, tz):
"""Get the min datetime of the given datetime and timezone.
:return datetime: Minimum datetime of the given date.
"""
return tz.localize(
datetime.combine(date.astimezone(tz).date(), time.min))
def max_datetime(date: datetime, tz):
"""Get the max datetime of the given datetime and timezone.
:return datetime: Maximum datetime of the given date.
"""
return tz.localize(
datetime.combine(date.astimezone(tz).date(), time.max))
Model.objects.filter(date__range=(min_datetime(value, tz), max_datetime(value, tz)))

Comparing unix timestamp with date in Django ORM

I am trying to fetch all records from a table on a particular date.
My url.py code is :
url(r'^jobs/date/?P<date>.*',RunningJobsListApiView.as_view()),
Here is the code of my view to get all the records from the table.
class RunningJobsListApiView(generics.ListAPIView):
queryset = LinuxJobTable.objects.annotate(status=Case(When(state=3, then=Value('Completed')),When(state=5, then=Value('Failed')),When(state=1, then=Value('Running')),default=Value('Unknown'),output_field=CharField(),),)
serializer_class = JobInfoSerializer
Now, I want to filter the jobs for the particular date in url. But In my database date is in UNIX timestamp format(ie.1530773247).
How can I compare DateFormat(mm-dd-yyyy) with UNIX timestamp format saved in DB?
To get a UNIX timestamp from a string date representation, you first need to convert the string to a Python datetime with strptime(), and then call the timestamp() method on it. But since a single day comprises a range of timestamps, you need to do a range query between the start of the target day and the start of the next day.
Something like:
from datetime import datetime, timedelta
target_day = datetime.strptime(date, "%m-%d-%Y")
next_day = target_day + timedelta(days=1)
queryset = LinuxJobTable.objects.filter(timestamp__range=(
int(target_day.timestamp()),
int(next_day.timestamp()) - 1 # since range is inclusive
))

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

Parse european string date to Django DateField

I am using USE_L10N = True in my app settings. As I understood, this setting let Django adapt date format according to the user current locale.
So in the admin, my model that contains a DateField is correctly represented in the form with the format "%d/%m/%Y".
The problem I have is when I want to create a new object from my code. I have a CSVParse custom function parsing a CSVĀ file and creating objects. One of the column in the CSV has the same format as above ("17/12/2015" for instance). I tried to parse the date with the line below but it returns "None". In the documentation of date_parse, I can see that it means a wrong format.
from django.utils import dateparse
date_csv = "18/12/2014"
date = dateparse.parse_date(date_csv)
What am I missing?
Thanks
Django dateparse uses date_re (regex) for parse_date and date_re format is year-month-day
date_re = re.compile(
r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$'
)
The input you are giving is having format day/month/year hence you are getting None as result.
Check out strptime
import time
date_csv = "18/12/2014"
date = time.strptime(date_csv, "%d/%m/%Y")

conversion of datetime Field to string in django queryset.values_list()

I have a queryset like:
qs = MyModel.objects.filter(name='me').values_list('activation_date')
here activation_date is DateTimeField in models.
When I download excel sheet from this qs I am not getting activation date in string format.
How can I convert this field('activation_date') in string or how to typecast it in qs?
https://docs.djangoproject.com/en/2.2/ref/models/fields/#datetimefield
A date and time, represented in Python by a datetime.datetime instance.
You can get a string representation of a DateTimeField casting it directly:
str(obj)
# obj = qs[0][0] ? or qs[0][1] ?
You'll get result like this (in this example I use datetime.datetime.now() since a DateTimeField is represented by datetime.datetime is the same behavior):
>>> now = datetime.datetime.now()
>>> str(now)
'2013-06-26 00:14:26.260524'
if you want less information or formatted in other mode you can use strftime() function for format them. see:
>>> now.strftime('%Y-%m-%d %H:%M')
'2013-06-26 00:14'
extra() is an old API that Django aims to deprecate at some point in the future. I would avoid using it.
Try the following instead:
from django.db.models import F, Func, Value, CharField
qs.annotate(
formatted_date=Func(
F('date'),
Value('dd.MM.yyyy hh:mm'),
function='to_char',
output_field=CharField()
)
)
This works only with a database that supports the to_char date type formatting function. Postgres provides this function by default.
If you use a MSSQL backend you could swap to_char with FORMAT.
For MySQL use DATE_FORMAT.
For Oracle consult their
documentation, etc.
After the queryset is evaluated this will add the annotation formatted_date to each object in the queryset that is returned.
extra is deprecated in Django 2.0
That's why I think the best solution to get a stringified datetime is:
foo_bar = FooBarModel.objects.annotate(
str_datetime=Cast(
TruncSecond('some_datetime_field', DateTimeField()), CharField()
)
).values('str_datetime').first()
The result is:
foo_bar.str_datetime:
(str)'2014-03-28 15:36:55'
Also I'd like to mention that you can format it as well in any way you want like:
from django.db.models import Value
foo_bar = FooBarModel.objects.annotate(
day=Cast(ExtractDay('some_datetime_field'), CharField()),
hour=Cast(ExtractHour('some_datetime_field'), CharField()),
str_datetime=Concat(
Value('Days: '), 'day', Value(' Hours: '), 'hour',
output_field=CharField()
)
).values('str_datetime').first()
The result is:
foo_bar.str_datetime:
(str)'Days: 28 Hours: 15'
If you are using Postgres, you can do it like this (date format options here). The solution is database dependent, but it sure beats looping though a long list in Python land after your perform the query.
qs = MyModel.objects.filter(name='me')
qs = qs.extra(select={'datestr':"to_char(activation_date, 'YYYY-MM-DD HH24:MI:SS')"})
qs = qs.values_list('datestr')
I am sure MySQL has some equivalent function as Postgres's to_char, but you'll have to find that on your own as I am not a MySQL guy.
Very surprised to see that no one suggested the cast to a simple TextField (note, I'm using Postgres so I can't confirm for other RDBMSes):
from django.db.models.functions import Cast
from django.db.models import TextField
queryset = FooBarModel.objects.values(my_datetime=Cast('some_datetime_field', TextField()))
foo_bar = queryset.first()
foo_bar['my_datetime']
>>> u'2019-10-03 17:59:37.979578+00'
It similarly also works fine for nested fields:
queryset = FooBarModel.objects.values(Cast('baz__some_datetime_field', TextField()))
Alternatively, a custom Func can also be used (also specific to Postgres here, but can be modified for any other RDBMS):
class FullDateTimeCast(Func):
"""
Coerce an expression to a new field type.
"""
function = 'TO_CHAR'
template = '%(function)s(%(expressions)s, \'FMDay, Month DD, YYYY at HH12:MI:SS AM\')'
queryset = FooBarModel.objects.values(my_datetime=FullDateTimeCast('some_datetime_field', TextField()))
foo_bar = queryset.first()
foo_bar['my_datetime']
>>> u' Thursday, October 03, 2019 at 17:59:37 PM'
qs = MyModel.objects.filter(name='me')
qs = qs.extra(select={'datestr':"DATE_FORMAT(activation_date, '%Y-%m-%d')"})
qs = qs.values_list('datestr')
You can also convert the date in queryset to string using map function. Example:
qs = MyModel.objects.filter(name='me').values_list('activation_date', flat=True)
data = map(str, qs)
I did it this way
.annotate(date_str=ExpressionWrapper(
Func(F('date'), Value('%d/%m/%Y %H:%i'), function='DATE_FORMAT'), output_field=CharField()
))
If you are doing this once, refer to Yannic Hamann's answer. However if you find yourself converting to str from the database a lot, you can define the Func as a class to avoid having to type output_field and function a bunch.
class DateToChar(models.Func):
"""
Custom Func expression to convert datetimes to str's in database query
Params for initializer
------
expression_1
expression resulting in a date: ex: F('date')
expression_2
Format string as an expression: Value('YYYY-MM-DD')
"""
arity = 2
function = 'to_char'
output_field = models.CharField()
Note that function will change depending on the database backend. This is written for Postgres.
This can be used like
qs = qs.annotate(date_str=DateToChar(F('date'), Value('YYYY-MM-DD'))
or with any expression that results in a date/datetime/Integer/Float/Decimal field (See Postgres to_char. Varies by database) and an expression resulting in a CharField or TextField.
See Func documentation for more information
I had a similar issue then I solved this in the following way:
from django.db.models.functions import Substr
list( Model.objects.values('when_date').annotate(date= Substr('when_date',1,10), total=Sum('amount')) )
(Django 4.1, python 3.10)