Discussion:
How can I write small widgets in Jinja1 or Jinja2 ?
Junya Hayashi
2008-09-12 12:13:06 UTC
Permalink
Hi,

I'm making custom web framework with pylons, and I switched the
template engine from Mako to Jinja.
But I've not found the good strategy for making small widgets.

For example, when I was using Mako, I wrote the "recent_news" widget
like following:

<%def name="recent_news(num=10)" filter="trim">
<%
entries = NewsModel.select(order_by="publish_at desc")
if num:
entries = entries[:num]
%>
<div>
% for entry in entries:
<ul>
<li><a href="${ h.url(controller='/news', action='show',
id=entry.id) }">${ entry.title }</a></li>
</ul>
% endfor
</div>
</%def>

and used it like this.

<%namespace name="news" file="/news/widget.html" />
${news.recent_news(5)}


And with Django, I can write the "custom template tag" for same
purpose.

{% load news %}
{% recent_news 5 %}


Mako or Django provides the place of writing arbitrary python code for
writing such small widgets.
Python code is closed in where it is defined, and you can use widgets
anywhere.


But I couldn't find the good strategy of doing like this with Jinja1
or Jinja2.
I know that I can define "globals" for passing arbitrary python
objects to templates,
but once I put some objects on globals, it will be loaded everywhere.

To avoid namespaces being dirty, I defined the globals like following.

class News(object):

@staticmethod
def recent(num):
query = NewsModel.query.order_by(["publish_at desc"])
return query.limit(num).all()

env.globals['news'] = News

And use it in the macro.

{% macro recent_news(num) %}
<div>
<ul>
{% for entry in news.recent(num) %}
<li><a href="{{ h.url(controller='/news', action='show',
id=entry.id) }}">{{ entry.title }}</a></li>
{% endfor %}
</ul>
</div>
{% endmacro %}


If I made many modules, globals would be bigger and bigger. So I think
this strategy is not so good.
Does someone have good idea ?
Armin Ronacher
2008-09-12 14:14:40 UTC
Permalink
Hi,
Post by Junya Hayashi
But I couldn't find the good strategy of doing like this with Jinja1
or Jinja2.
I know that I can define "globals" for passing arbitrary python
objects to templates,
but once I put some objects on globals, it will be loaded everywhere.
There are many ways to do that sort of thing, and Jinja2 doesn't really
recomment one in the documentation. The easiest way would be putting the
module of your models into the globals:

env.globals['m'] = sys.modules['application.models']

Assuming your application is already fully imported if you render a
template, your macro can do something like this::

{% macro recent_news(num) %}
{% set news = m.NewsModel.select(...).limit(num) %}
...
{% endmacro %}

And then like you did with mako::

{% import 'news/widget.html' as news %}
${news.recent_news(5)}


Regards,
Armin
Junya Hayashi
2008-09-16 02:48:54 UTC
Permalink
Thanks Armin.
Post by Armin Ronacher
env.globals['m'] = sys.modules['application.models']
Flexibility and sandbox seems to be trade-off.
So, I'll design the module carefully.

Best regards,
Junya
Post by Armin Ronacher
Hi,
Post by Junya Hayashi
But I couldn't find the good strategy of doing like this with Jinja1
or Jinja2.
I know that I can define "globals" for passing arbitrary python
objects to templates,
but once I put some objects on globals, it will be loaded everywhere.
There are many ways to do that sort of thing, and Jinja2 doesn't really
recomment one in the documentation. The easiest way would be putting the
env.globals['m'] = sys.modules['application.models']
Assuming your application is already fully imported if you render a
{% macro recent_news(num) %}
{% set news = m.NewsModel.select(...).limit(num) %}
...
{% endmacro %}
{% import 'news/widget.html' as news %}
${news.recent_news(5)}
Regards,
Armin
Loading...