Python Mako


Python Mako

I’ve been using Django for a long time. The templating language is easy to learn, use and extend, but Its kinda ugly coding and feels very limiting when your used to python functionality. Jinja2 is a bit better. Unlike Django it allows you to call methods in a class and pass parameters to them. Macros are also good. Yet I was still unsatisfied with it.

When I thought about it and looked at both of these, I saw they had a common ethos. Separation of the logic layer with the presentation layer. That means they want to separate the view from the template. The view does all the calculations and getting data, and the template displays the results.

This doesn’t really work though, does it. The more data driven our applications, the more the data and logics impact the presentation layer. To resolve this Django and Jinja2 rely on the creation of a lot more templates. This in turn results in a lot of duplication. Now I know why I’m not satisfied with them.

There are quite a few other templating languages out there, but I found one that is well supported and provided me with what I wanted. Mako.

Philosophy behind Mako

Mako doesn’t go on about separation of login and presentation, instead it has a very simple philosophy

Python is a great scripting language. Don’t reinvent the wheel…your templates can handle it !

What this means in real terms in that you can put python code in your template.

Now some will scream and disagree with this, but its what I was looking for. I wanted to be able to take what I could do in python and let loose in the templating.

A Quick Warning

Django and Jinja2 don’t complain if a variable has not been passed to the template. Mako does. If you have defined a variable to use, you MUST pass it when rendering.

Python in Your template

Mako does have templating logic you can use. Conditionals, Loops etc. It also has its own version of Jinja2’s macro language. I will go into that a little later, as Mako does take macros to a very different place. First off, lets look at putting some python in a template

this is my template
<%
name = f"my special template: {context_text}"
%>
${name}

This is known as a Python block. between the <% %> you can add your code in and it has access to the context passed to the template. What you can’t do inside the block is display anything. The example above shows initializing a variable called name. This then has to be displayed using ${name} outside of the block.

So what can you do in the block. Well anything you can do in Python. You can import modules, create objects, raise exceptions etc. What I’m hoping to do is to be able to add stuff like this.

My Table
<%
from components import MyTable
t = MyTable(headers, data)
%>
${t.render()}

I can pack all the logic in creating a HTML table into a class, import it into the template and just render. I guess I could do this with Jinja2 etc, but it just feels better to do it like this.

Module Blocks

This is something of a complicated idea, so its worth going through a couple of times. Mako converts (transpiles) templates into Python module or class. So the template class has to be initialized to create an object. Once we have that, we run the render() method.

Mako allows us to add code that will run when render() is called and also when the class is initialized. The code above is of course run when the render() is called because it has the context data, but we can do imports create functions and define constants when the instant is create.

<%!
import mylib
import re

def filter(text):
return re.sub(r'^@', '', text)
%>

I think there is no need to worry about what runs when too much. The important thing is that we can add python classes and functions into our code and thus extend by quite a bit, what the template is possible of.

Defs: Mako Macros

As the title says in jinja2 we have macros in Mako we have Defs. Here is an example of a simple Def

Hello there ${username}, how are ya.  Lets see what your account says:

${account()}

<%def name="account()">
Account for ${username}:<br/>

% for row in accountdata:
Value: ${row}<br/>
% endfor
</%def>

A point to note here is that Defs can be defined before or after the point they are called in the template. Here is another example using arguments.

${account(accountname='john')}

<%def name="account(accountname, type='regular')">
account name: ${accountname}, type: ${type}
</%def>

Def Namespaces

Mako does allow you to pull Defs from other files and you can add these to the base template object. However Mako allows you to pull a group of Defs in and place them in a namespace and call them like you would call a function in a module.

<%namespace name="macs1" file="macs1.html"/>

${macs1.def1(5)}

<%namespace file="macs2.html" import="def2"

${def2(5)}

These basically represent different imports you would do in python.

import macs1
from macs2 import def2

Calling Defs in your views or python logic.

Here is another interesting feature. You can access a Def and render it while in your python logic or view.

from mako.template import Template

template = Template("""
<%def name="hi(name)">
hi ${name}!
</%def>

<%def name="bye(name)">
bye ${name}!
</%def>
"""
)

print(template.get_def("hi").render(name="ed"))
print(template.get_def("bye").render(name="ed"))

I like this. I work on a lot of backends to web applications. While some might feel this is wrong, I prefer to send a rendered snippet of html, instead of sending json and rendering on the frontend. This function allows me to have a bunch of snippet templates in the same file and just render the one I want, rather than have them all in different files.

Conclusion

Mako goes very much against the grain if what many people consider proper practice in web development. Guidelines are just guidelines however and they don’t necessarily match all web applications. Web components for example, have embedded javascript in them (logic) to define not only how they are displayed, but how the data is to be processed and moved around. I think Mako just allows us to do the same thing on the backend.

As a negative point, I’ve got to say Mako is VERY complicated. Django templating and Jinja2 are pretty easy to get to grips with. Mako is not. There is a reason for that though. Mako is just more flexible and powerful.