Can your Python template do this?
I like to imagine components of a Python web framework as personalities. Here's how I would imagine a conversation between a typical view and template would go down:
View Here's that data I just looked up.
Template Don't like it.
View What's wrong with it?
Template Format's wrong.
View You can change the format if you like.
Template Don't wanna. You change it.
View OK. Fine. I'll change it for you.
Which is why I wanted Moya templates to be very capable in processing data, but still keep them designer friendly. If the template data is sane then the template should be able to deal with it, without going back to the back-end developer.
I've got to a position where I love working with Moya templates. I honestly think you can do more with Moya templates than any other Python template language (without writing an extension).
So I've come up with a few (admittedly far fetched) examples of Moya templates, and I'd like to ask; can your python template do this?
Render a mandelbrot set?
<style>
table {border-spacing:0px;}
td {width:8px; height:8px;}
</style>
<table>
{% let xsize=80, ysize=60, max_iteration=50, color=color:'red' %}
{% for pixy in 1..ysize %}
{%- let y0 = (pixy / ysize) * 2 - 1 %}
<tr>
{%- for pixx in 1..xsize %}
{%- let x0 = (pixx / xsize) * 3 - 2, x=0, y=0, iteration=0 %}
{%- while x * x + y * y < 4 and iteration < max_iteration %}
{%- let x = x * x - y * y + x0, y = 2 * x * y + y0 %}
{%- let iteration = iteration + 1 %}
{%- end-while %}
<td style="background-color:${color * (iteration / max_iteration)}"></td>
{%- end-for %}
</tr>
{% end-for %}
</table>
![Screen Shot 2016-12-14 at 23.49.35.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/2b63fbcc-c258-11e6-ae4f-f23c91845b44.png/2b63fbcc-c258-11e6-ae4f-f23c91845b44png.lg.3.jpeg)
This is a terrible idea, but it makes for a nice screenshot. The output is 1.4 MB html file.
The {% let %}
tag assigns a variable, for instance {% let x = 0 %}
sets the value x
to 0
. You probably guessed as much.
An interesting (to me at least) behaviour of {% let %}
is that it evaluates all the expressions before storing them. So you can swap a variable with {% let x=y, y=x %}
.
Render all the dates in the current month?
<ul>
{% for date in .now.month_start.date ... .now.next_month.date %}
<li>
${localize:date}
</li>
{% endfor %}
</ul>
![Screen Shot 2016-12-14 at 21.38.23.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/e1ab04b0-c245-11e6-a0bd-f23c91845b44.png/e1ab04b0-c245-11e6-a0bd-f23c91845b44png.lg.3.jpeg)
What sorcery is this? The .now
is a request local value that contains the current date + time. It has an attribute called month_start
which is the first day of the month. That has a date
attribute which contains just the date. So .now.month_start.date
gives the first date of the current month.
The ...
is an exclusive range operator, which creates a range of values between two end points.
Oh, and the localize:
modifier renders the date according to the current locale.
Tell you how many days until XMas?
{%- with time_till_xmas = datetime:'2016-12-25' - .now.utc.day_start %}
<h1>
${int:time_till_xmas.days} days to XMas
</h1>
{%- end-with %}
![Screen Shot 2016-12-15 at 18.10.56.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/e5b3f060-c2f1-11e6-9e23-f23c91845b44.png/e5b3f060-c2f1-11e6-9e23-f23c91845b44png.lg.3.jpeg)
The are date / time / timedelta objects that are essentially augmented versions of the Python equivalents.
Time deltas have an intuitive syntax. For instance .now + 28d
gets the date 28 days from now. By that I mean it does a date calculation (It doesn't take 4 weeks to run).
Render Markdown?
{% markup-block as 'markdown' %}
# Markdown is awesome
You *should* be able to use it **everywhere**.
{% end %}
Inline styles for Emails?
{% premailer %}
<style>
.highlight
{
padding:16px;
background-color:red;
color:white;
font-size:32px;
}
</style>
<div class="highlight">
Buy my product!
</div>
{% end-premailer %}
![Screen Shot 2016-12-14 at 21.52.25.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/a693b7ee-c247-11e6-8fcd-f23c91845b44.png/a693b7ee-c247-11e6-8fcd-f23c91845b44png.lg.3.jpeg)
Because email clients don't like style tags or external stylesheets. The {% premailer %}
tag inlines them for you.
Work with colors?
{% let start = color:'lime' %}
{% let end = color:'#0000ff' %}
{% for i in 1..100 %}
{% let color = start + (end - start) * (i / 100) %}
<div style="background-color:${color};font-size:12px;">
${color.rgb}
</div>
{% end-for %}
![Screen Shot 2016-12-14 at 22.54.12.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/5dd2f282-c250-11e6-ba19-f23c91845b44.png/5dd2f282-c250-11e6-ba19-f23c91845b44png.lg.3.jpeg)
The color object lets you do color manipulation that you couldn't do without some kind of pre-processor. It does tread on the toes of CSS pre-processors somewhat. But its there if you need it.
Inspect template data?
{% inspect dict:.now %}
![Screen Shot 2016-12-14 at 23.15.33.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/42d36964-c253-11e6-a0bd-f23c91845b44.png/42d36964-c253-11e6-a0bd-f23c91845b44png.lg.3.jpeg)
Handy for debugging, the {% inspect %}
tag summarizes any template variable (request, database object etc) in the output. Here we see all the properties of a date/time object.
Get the current price of Bitcoin
{% cache for 10m %}
<ul>
{% for k, v in sorted:items:parsejson:get:"https://www.bitstamp.net/api/ticker/" %}
<li><b>${k}</b> ${v}
{% endfor %}
</ul>
{% end-cache %}
The get:
modifier does a HTTP GET to a URL. In practice it's not something I would recommend, as it's a blocking operation, and connectivity issues could cause it to fail. That one might be best left to the view.
Tell you exactly where the error is?
A cryptic error message can cost you minutes fixing a bug. If you are iterating quickly, it could add up to an hour or more in a day. Which is why Moya templates are fanatical about error reporting.
![Screen Shot 2016-12-15 at 00.05.24.png](https://media.moyaproject.com/willmcgugan/uploads/thumbnails/techblog/49470826-c25a-11e6-ae4f-f23c91845b44.png/49470826-c25a-11e6-ae4f-f23c91845b44png.lg.3.jpeg)
Clear error messages that tell you what and where the problem is.
If your template is nested, i.e. included from another template, the error messages will show you the full include stack.
And more often or not, there is a diagnosis that tells you how to fix the issue.
You can check out Moya on Github.
I could write a Django filter to do that.
That’s simply abuse of templates.
That’s supposed to be done in CSS, not HTML.
That’s a violation of MVC.
Of course you could! But the same argument can be said of any feature that Django doesn't have builtin.
I'm not suggesting it is a good idea. But the templates can take the abuse.
You can do stuff with the color object you can not do without some kind of pre-processor.
In what way? And do you mean MVT? If you we're talking about Django here.
Mmmmm. Very php.
Only superficially.
No, my template system can't and it should stay that way. Isn't the whole point of templates to be presentation without logic? The model or view handle the logic and templates only render the data to present it in a particular way. This seems really backwards to bring the logic back to the templates.
I'm not suggesting putting the logic in the templates. Business logic belongs in the view. But if the data is in the template context, I would prefer to have powerful enough templates that I can present the information in any way I wish.
This sucks! No, really, it looks very nice, I like the error report thing very much. I'll give it a try. Thanks, and great job!
Thanks, Agustin. Let me know how you get on!