Using Jinja2, how do I format a date field? I know in Python I can simply do this:
print(car.date_of_manufacture.strftime('%Y-%m-%d'))
But how do I format the date in Jinja2?
There are two ways to do it. The direct approach would be to simply call (and print) the strftime() method in your template, for example
{{ car.date_of_manufacture.strftime('%Y-%m-%d') }}
Another, sightly better approach would be to define your own filter, e.g.:
from flask import Flask
import babel
app = Flask(__name__)
#app.template_filter()
def format_datetime(value, format='medium'):
if format == 'full':
format="EEEE, d. MMMM y 'at' HH:mm"
elif format == 'medium':
format="EE dd.MM.y HH:mm"
return babel.dates.format_datetime(value, format)
(This filter is based on babel for reasons regarding i18n, but you can use strftime too). The advantage of the filter is, that you can write
{{ car.date_of_manufacture|format_datetime }}
{{ car.date_of_manufacture|format_datetime('full') }}
which looks nicer and is more maintainable. Another common filter is also the "timedelta" filter, which evaluates to something like "written 8 minutes ago". You can use babel.dates.format_timedelta for that, and register it as filter similar to the datetime example given here.
Here's the filter that I ended up using for strftime in Jinja2 and Flask
#app.template_filter('strftime')
def _jinja2_filter_datetime(date, fmt=None):
date = dateutil.parser.parse(date)
native = date.replace(tzinfo=None)
format='%b %d, %Y'
return native.strftime(format)
And then you use the filter like so:
{{car.date_of_manufacture|strftime}}
I think you have to write your own filter for that. It's actually the example for custom filters in the documentation.
If you are dealing with a lower level time object (I often just use integers), and don't want to write a custom filter for whatever reason, an approach I use is to pass the strftime function into the template as a variable, where it can be called where you need it.
For example:
import time
context={
'now':int(time.time()),
'strftime':time.strftime } # Note there are no brackets () after strftime
# This means we are passing in a function,
# not the result of a function.
self.response.write(jinja2.render_template('sometemplate.html', **context))
Which can then be used within sometemplate.html:
<html>
<body>
<p>The time is {{ strftime('%H:%M%:%S',now) }}, and 5 seconds ago it was {{ strftime('%H:%M%:%S',now-5) }}.
</body>
</html>
You can use it like this in template without any filters
{{ car.date_of_manufacture.strftime('%Y-%m-%d') }}
Google App Engine users : If you're moving from Django to Jinja2, and looking to replace the date filter, note that the % formatting codes are different.
The strftime % codes are here: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
You can use it like this in jinja template
{{ row.session_start_date_time.strftime('%d-%m-%Y %H:%M:%S')}}
In this code the field name is row.session_start_date_time.
in flask, with babel, I like to do this :
#app.template_filter('dt')
def _jinja2_filter_datetime(date, fmt=None):
if fmt:
return date.strftime(fmt)
else:
return date.strftime(gettext('%%m/%%d/%%Y'))
used in the template with {{mydatetimeobject|dt}}
so no with babel you can specify your various format in messages.po like this for instance :
#: app/views.py:36
#, python-format
msgid "%%m/%%d/%%Y"
msgstr "%%d/%%m/%%Y"
I use this filter, it's in Spanish but you may change the names as you need.
#app.template_filter('datetime')
def date_format(value):
months = ('Enero','Febrero',"Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre")
month = months[value.month-1]
hora = str(value.hour).zfill(2)
minutos = str(value.minute).zfill(2)
return "{} de {} del {} a las {}:{}hs".format(value.day, month, value.year, hora, minutos)
There is a jinja2 extension you can use just need pip install (https://github.com/hackebrot/jinja2-time)
Related
I am new in the Airflow world and trying to understand one thing. For example I have a DAG that contains 2 tasks. The first task is submitting spark job, and the second one is Sensor that waits for a file in s3.
RUN_DATE_ARG = datetime.utcnow().strftime(DATE_FORMAT_PY)
DATE = datetime.strptime(RUN_DATE_ARG, DATE_FORMAT_PY) - timedelta(hours=1)
with DAG() as dag:
submit_spark_job = EmrContainerOperator(
task_id="start_job",
virtual_cluster_id=VIRTUAL_CLUSTER_ID,
execution_role_arn=JOB_ROLE_ARN,
release_label="emr-6.3.0-latest",
job_driver=JOB_DRIVER_ARG,
configuration_overrides=CONFIGURATION_OVERRIDES_ARG,
name=f"spark-{RUN_DATE_ARG}",
retries=3
)
validate_s3_success_file = S3KeySensor(
task_id='check_for_success_file',
bucket_name="bucket-name",
bucket_key=f"blabla/date={DATE.strftime('%Y-%m-%d')}/hour={DATE.strftime('%H')}/_SUCCESS",
poke_interval=10,
timeout=60,
verify=False,
)
I have a RUN_DATE_ARG that by default should be taken from datetime.utcnow() and this is one of sparks java arguments that I should provide to my job.
I want to add an ability to submit job with custom date argument (via airflow UI).
When I am trying to retrieve it as '{{ dag_run.conf["date"] | None}}' it replaces with value inside task configuration (bucket_key=f"blabla/date={DATE.strftime('%Y-%m-%d')}/hour={DATE.strftime('%H')}/_SUCCESS",), but not for DAG's python code if I do following:
date='{{ dag_run.conf["date"] | None}}'
if date is None:
RUN_DATE_ARG = datetime.utcnow().strftime(DATE_FORMAT_PY)
else:
RUN_DATE_ARG = date
Do I have any way to use this value as a code variable?
You can not use templating outside of operators scope.
You should use Jinja if statements in the operator templated parameter. The following is just a general idea:
submit_spark_job = EmrContainerOperator(
task_id="start_job",
...
name="spark-{{ dag_run.conf["date"] if dag_run.conf["date"] is not None else jinja_utc_now }}",
)
You will need to replace jinja_utc_now with code that retrieve the timestamp probably something like what is shown in this answer.
You can also use:
{% if something %}
code
{% else %}
another code
{% endif %}
From Airflow point of view it takes the parameter and pass it though Jinja engine for templating so the key issue here is just to use the proper Jinja syntax.
I'm trying to refacto some pretty heavy template with jinja2 and I'm stucked on an include.
This is the behaviour i'm expecting :
<h1>{{ key }} </h1>
{% set file = key | include_text %}
{% include file %}
The custom filter returns a string like this one ::
texts/my_include.html
But instead I got this error:
jinja2.exceptions.TemplatesNotFound: Tried to select from an empty list of templates
Some hack I've already tried :
Place the templates in the same folder and remove the 'texts/' from the returned string
Add the path in the Env loader
But it keeps sending this error
I'm now wondering if jinja2 allows this implementation or if I'll have to keep this template the way it was (even if it takes a very long time to be generated).
Does someone know about some trick here ?
Well, for those who eventually met this problem in the futur, I've solved it by removing the unecessary single quotes and by sending some empty file from my custom filter when the condition is not verified... (my mistake)
Here is my custom filter :
#environmentfilter
def include_text(ctx, key):
res_dict = {
'key_value_1' : 'file_name_1',
'key_value_2' : 'file_name_2'
}
try:
return "texts/" + res_dict[key] + ".html"
except KeyError:
return "texts/empty.html"
Now, the first solution I was trying works fine.
how to write GAE search query using date?
This is my data store.
Class Property(db.Model):
createdDate=db.DateTimeProperty(auto_now_add=True)
i want to display the records in last sevendays. am tring this,
sevendays = datetime.now() - timedelta(hours=168)
getting correct result for date(7days), and i passed search query in my URL like this,
<a href="/search?search=PropertyCaseType+%3D+Enquiry+AND+PropertyStatus+%3D+Open+AND+createdDate+%3E+sevendays" class="list-group-item">
<i class="fa fa-question-circle fa-fw"></i> New Enquires
<span class="pull-right text-muted small"><em>
{% if countEnquiryOpen %}
{{ countEnquiryOpen }}
{% else %}
0
{% endif %}
</em>
</span>
</a>
above href= encode query is :
PropertyCaseType = Enquiry AND PropertyStatus = Open AND createdDate > sevendays
createdDate > sevendays is not working , otherwise query working fine and getting correct results.
how to do this , friends help me
I got the solution.. The problem is Time. So i remove time filter only date. like this,
sevendays = datetime.now() - timedelta(hours=168)
sevendays = sevendays.date()
Now i get correct result. :)
It is a bit hard to work out what is wrong with your app, as you don't show how are using the query string to construct a query.
However, a likely source of the problem could be that you are trying to compare a date string, rather than a datetime object to the createdDate property.
E.g.
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
all_props = Property.all().run()
self.response.out.write("ALL Properties\n")
for d in all_props:
self.response.out.write('Prop: %s\n' % d.createdDate)
sevendays = datetime.now() - timedelta(hours=168)
res = Property.gql('where createdDate > :1', sevendays)
self.response.out.write("MATCHING Properties compared by datetime\n")
for d in res:
self.response.out.write('date: %s\n' % d.createdDate)
self.response.out.write("MATCHING Properties compared by string\n")
sds = str(sevendays)
res_bad = Property.gql('where createdDate > :1', sds)
for d in res_bad:
self.response.out.write('date: %s\n' % d.createdDate)
will print out the correct properties under MATCHING Properties compared by datetime, but not under MATCHING Properties compared by string. Unfortunately it doesn't raise any errors either, to tell you that you are using the wrong kind of object in the query.
Therefore, if you are using the query string straight as it is passed in the url, you may need to parse it into a datetime object first using e.g. datetime.strptime().
You should build an index using only the elements from the last 7 days, then query that index and rebuild index accordingly. You can read about indexing in google app engine here.
I have URLs stored in a Django model that I would like to display on a template, but I would only like to display the domain like this:
original_url:
https://wikipedia.org/wiki/List_of_chemical_process_simulators
display:
wikipedia.org/...
Would it be better to handle this on entirely on the back-end, font-end, or a custom function on with jinja2?
If it is something you would later reuse in the templates throughout the project and, taking into account that there is a pretty simple logic involved, defining a custom template filter would be perfectly okay here.
Use urlparse.urlparse() to get the domain name:
>>> from urlparse import urlparse
>>> from jinja2 import Environment, Template
>>>
>>> def get_domain(url):
... return "%s/..." % urlparse(url).netloc
...
>>>
>>> env = Environment()
>>> env.filters['domain'] = get_domain
>>>
>>> template = env.from_string('{{ url|domain }}')
>>> template.render(url='https://wikipedia.org/wiki/List_of_chemical_process_simulators')
u'wikipedia.org/...'
This is a simple example, you should additionally provide an error-handling mechanism in case urlparse() would fail parsing the url passed in.
The best way would probably be a custom template filter as answered by #alecxe, but in case you can't, here is a non-fool-proof way for future reference.
{{ original_url.rpartition("//")[-1] }}
https://wikipedia.org/wiki/List_of_chemical_process_simulators → wikipedia.org/wiki/List_of_chemical_process_simulators
//example.net/path/to/file → example.net/path/to/file
ftp://example.net/pub → example.net/pub
Get just the domain name (hostname):
{{ original_url.rpartition("//")[-1].partition("/")[0] }}
https://wikipedia.org/wiki/List_of_chemical_process_simulators → wikipedia.org
//example.net/path/to/file → example.net
ftp://example.net/pub → example.net
I'd like to use a number with a decimal point in a Django URL pattern but I'm not sure whether it's actually possible (I'm not a regex expert).
Here's what I want to use for URLs:
/item/value/0.01
/item/value/0.05
Those URLs would show items valued at $0.01 or $0.05. Sure, I could take the easy way out and pass the value in cents so it would be /item/value/1, but I'd like to receive the argument in my view as a decimal data type rather than as an integer (and I may have to deal with fractions of a cent at some point). Is it possible to write a regex in a Django URL pattern that will handle this?
It can be something like
urlpatterns = patterns('',
(r'^item/value/(?P<value>\d+\.\d{2})/$', 'myapp.views.byvalue'),
... more urls
)
url should not start with slash.
in views you can have function:
def byvalue(request,value='0.99'):
try:
value = float(value)
except:
...
I don't know about Django specifically, but this should match the URL:
r"^/item/value/(\d+\.\d+)$"
If the values to be accepted are only $0.01 or $0.05, the harto's pattern may be specified like this:
r"^/item/value/(\d\.\d{2})$"
Don't use »
url(r"^item/value/(?P<dollar>\d+\.\d{1,2})$", views.show_item, name="show-item"),
It will only match the URL patterns like /item/value/0.01, /item/value/12.2 etc.
It won't match URL patterns like /item/value/1.223, /item/value/1.2679 etc.
Better is to use »
url(r"^item/value/(?P<dollar>\d+\.\d+)$", views.show_item, name="show-item"),
It will match URL patterns like /item/value/0.01, /item/value/1.22, /item/value/10.223, /item/value/1.3 etc.
Finally you can design your views.py something like
This is just for an example.
# Make sure you have defined Item model (this is just an example)
# You use your own model name
from .models import Item
def show_item(request, dollar):
try:
# Convert dollar(string) to dollar(float).
# Which gets passed to show_item() if someone requests
# URL patterns like /item/value/0.01, /item/value/1.22 etc.
dollar = float(dollar);
# Fetch item from Database using its dollar value
# You may use your own strategy (it's mine)
item = Item.objects.get(dollar=dollar);
# Make sure you have show_item.html.
# Pass item to show_item.html (Django pawered page) so that it could be
# easily rendered using DTL (Django template language).
return render(request, "show_item.html", {"item": item});
except:
# Make sure you have error.html page (In case if there's an error)
return render(request, "error.html", {});