Filter by CharField pretending it is DateField in Django ORM/mySql
I am working with a already-done mySQL Database using Django ORM and I need to filter rows by date - if it wasn't that dates are not in Date type but normal Varchar(20) stored as dd/mm/yyyy hh:mm(:ss). With a free query I would transform the field into date and I would use > and < operators to filter the results but before doing this I was wondering whether Django ORM provides a more elegant way to do so without writing raw SQL queries.
I look forward to any suggestion.
EDIT: my raw query would look like
SELECT * FROM table WHERE STR_TO_DATE(mydate,'%d/%m/%Y %H:%i') > STR_TO_DATE('30/12/2020 00:00', '%d/%m/%Y %H:%i')
Thank you.
Solution 1:
I will assume your model looks like this:
from django.db import models
class Event(models.Model):
mydate = models.CharField(max_length=20)
def __str__(self):
return f'Event at {self.mydate}'
You can construct a Django query expression to represent this computation. This expression consists of:
-
Func
objects representing yourSTR_TO_DATE
function calls. - An
F
object representing your field name. - A
GreaterThan
function to represent your>
comparison.
from django.db.models import F, Func, Value
from django.db.models.lookups import GreaterThan
from .models import Event
# Create some events for this example
Event(mydate="29/12/2020 00:00").save()
Event(mydate="30/12/2020 00:00").save()
Event(mydate="31/12/2020 00:00").save()
class STR_TO_DATE(Func):
"Lets us use the STR_TO_DATE() function from SQLite directly in Python"
function = 'STR_TO_DATE'
# This Django query expression converts the mydate field
# from a string into a date, using the STR_TO_DATE function.
mydate = STR_TO_DATE(F('mydate'), Value('%d/%m/%Y %H:%i'))
# This Django query expression represents the value 30/12/2020
# as a date.
date_30_12_2020 = STR_TO_DATE(Value('30/12/2020 00:00'), Value('%d/%m/%Y %H:%i'))
# This Django query expression puts the other two together,
# creating a query like this: mydate < 30/12/2020
expr = GreaterThan(mydate, date_30_12_2020)
# Use the expression as a filter
events = Event.objects.filter(expr)
print(events)
# You can also use the annotate function to add a calculated
# column to your query...
events_with_date = Event.objects.annotate(date=mydate)
# Then, you just treat your calculated column like any other
# field in your database. This example uses a range filter
# (see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#range)
events = events_with_date.filter(date__range=["2020-12-30", "2020-12-31"])
print(events)
I tested this answer with Django 4.0.1 and MySQL 8.0.