Django template – Is there a built-in way to get current date as type 'date' instead of type 'str'?
Question:
I know I can get the current date as a str
in a Django template (using the template tag now
), like this:
{% now "Y-m-d" as today_str %}
<p>{{ today_str }}</p>
But I cannot use that for comparissons:
{% now "Y-m-d" as today_str %}
{% for elem in object_list %}
{% if elem.date < today_str %} {# WRONG: this compares 'date' and 'str' #}
<p>{{ elem.pk }} before today</p>
{# do some other rendering #}
{% endif %}
{% endfor %}
Possible solutions:
-
I know I can pass a context variable to the template, but it requires code in my view:
# in my class-based-view in 'views.py'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['today'] = timezone.now()
return ctx
-
Or I can create a custom template tag, but that is even more additional code.
As you can see I have workarounds for my problem, but I would like to know if there is a buit-in way to get the current date
(or datetime
) in the template?
Answers:
So, all my searching didn’t yield a short solution. The answer to the question seems to be: no, there is no buit-in way to get the current date (or datetime) as a variable in the template.
In case others are searching for this topic, I’ll try to give a summary of the possible workarounds that I can up with and that were suggested by other users.
-
I could pass a context variable to the template from my view. In class-based views that could look like this (it is even an example in the docs):
# file 'my_app/views.py'
from django.utils import timezone as tz
from django.views.generic import ListView
class MyView(ListView)
...
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
now = tz.now()
ctx['now'] = now
ctx['today'] = tz.localtime(now).date()
return ctx
-
I could create a custom context processor that loads that variable to every template. In class-based views that could look like this:
# file 'context_processors.py'
from django.utils import timezone as tz
def now_and_today(request):
now = tz.now()
return {
'now': now,
'today': tz.localtime(now).date(),
}
# file 'settings.py'
...
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'context_processors.now_and_today',
],
},
},
]
...
-
I could create a custom template tag, like this:
# file 'my_app/custom_template_tags/custom_time_tags.py'
from django.utils import timezone as tz
from django import template
register = template.Library()
@register.simple_tag
def get_now(request):
return tz.now()
@register.simple_tag
def get_today(request):
return tz.localtime(tz.now()).date()
To be used like this:
{% load 'custom_time_tags' %}
{% get_today as today %}
{% for per in person_list %}
{% if per.brith_date > today %}
<p>{{ per.name }} is from the future!!<p>
{% endif %}
{% endfor %}
-
I could also add a property (or even a cached_property
) to the model:
# file 'models.py'
from django.db import models
from django.utils import timezone as tz
from django.utils.functional import cached_property
class Person(models.Model):
...
@cached_property
def is_from_future(self):
# careful: for long-lived instances do not use 'cached_property' as
# the time of 'now' might not be right later
if self.birth_date > tz.localtime(tz.now()).date():
return True
return False
-
And last but not least, I could just do the processing in the view and add a property to the elements:
# file 'my_app/views.py'
from django.utils import timezone as tz
def person_list(request):
today = tz.localtime(tz.now()).date()
person_list = []
for p in Person.objects.all():
p.is_from_future = self.birth_date > today
person_list.append(p)
return render(request, 'some_template.html', {'person_list': person_list})
From django documentation:
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#now
You can also use the syntax {% now "Y" as current_year %} to store the output (as a string) inside a variable. This is useful if you want to use {% now %} inside a template tag like blocktranslate for example:
{% now "Y" as current_year %}
{% blocktranslate %}Copyright {{ current_year }}{% endblocktranslate %}
I know I can get the current date as a str
in a Django template (using the template tag now
), like this:
{% now "Y-m-d" as today_str %}
<p>{{ today_str }}</p>
But I cannot use that for comparissons:
{% now "Y-m-d" as today_str %}
{% for elem in object_list %}
{% if elem.date < today_str %} {# WRONG: this compares 'date' and 'str' #}
<p>{{ elem.pk }} before today</p>
{# do some other rendering #}
{% endif %}
{% endfor %}
Possible solutions:
-
I know I can pass a context variable to the template, but it requires code in my view:
# in my class-based-view in 'views.py' def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['today'] = timezone.now() return ctx
-
Or I can create a custom template tag, but that is even more additional code.
As you can see I have workarounds for my problem, but I would like to know if there is a buit-in way to get the current date
(or datetime
) in the template?
So, all my searching didn’t yield a short solution. The answer to the question seems to be: no, there is no buit-in way to get the current date (or datetime) as a variable in the template.
In case others are searching for this topic, I’ll try to give a summary of the possible workarounds that I can up with and that were suggested by other users.
-
I could pass a context variable to the template from my view. In class-based views that could look like this (it is even an example in the docs):
# file 'my_app/views.py' from django.utils import timezone as tz from django.views.generic import ListView class MyView(ListView) ... def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) now = tz.now() ctx['now'] = now ctx['today'] = tz.localtime(now).date() return ctx
-
I could create a custom context processor that loads that variable to every template. In class-based views that could look like this:
# file 'context_processors.py' from django.utils import timezone as tz def now_and_today(request): now = tz.now() return { 'now': now, 'today': tz.localtime(now).date(), } # file 'settings.py' ... TEMPLATES = [ { ... 'OPTIONS': { 'context_processors': [ ... 'context_processors.now_and_today', ], }, }, ] ...
-
I could create a custom template tag, like this:
# file 'my_app/custom_template_tags/custom_time_tags.py' from django.utils import timezone as tz from django import template register = template.Library() @register.simple_tag def get_now(request): return tz.now() @register.simple_tag def get_today(request): return tz.localtime(tz.now()).date()
To be used like this:
{% load 'custom_time_tags' %} {% get_today as today %} {% for per in person_list %} {% if per.brith_date > today %} <p>{{ per.name }} is from the future!!<p> {% endif %} {% endfor %}
-
I could also add a property (or even a
cached_property
) to the model:# file 'models.py' from django.db import models from django.utils import timezone as tz from django.utils.functional import cached_property class Person(models.Model): ... @cached_property def is_from_future(self): # careful: for long-lived instances do not use 'cached_property' as # the time of 'now' might not be right later if self.birth_date > tz.localtime(tz.now()).date(): return True return False
-
And last but not least, I could just do the processing in the view and add a property to the elements:
# file 'my_app/views.py' from django.utils import timezone as tz def person_list(request): today = tz.localtime(tz.now()).date() person_list = [] for p in Person.objects.all(): p.is_from_future = self.birth_date > today person_list.append(p) return render(request, 'some_template.html', {'person_list': person_list})
From django documentation:
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#now
You can also use the syntax {% now "Y" as current_year %} to store the output (as a string) inside a variable. This is useful if you want to use {% now %} inside a template tag like blocktranslate for example:
{% now "Y" as current_year %}
{% blocktranslate %}Copyright {{ current_year }}{% endblocktranslate %}