Posts tagged ‘Flask’

Testing Jinja Templates

2013-07-06 21:43

If you use a powerful HTML templating engine – like Jinja – inevitably you will notice a slow creep of more and more complicated logic entering your templates. Contrary to what many may tell you, it’s not inherently bad. Views can be complex, and keeping that complexity contained within templates is often better than letting it sip into controller code.

But logic, if not trivial, requires testing. Exempting it by saying “That’s just a template!” doesn’t really cut it. It’s pretty crappy excuse, at least in Flask/Jinja, where you can easily import your template macros into Python code:

  1. {% macro hello_world() %}
  2.     Hello world!
  3. {% endmacro %}
  1. import flask
  2. hello_world = flask.get_template_attribute('hello.html', 'hello_world')
  3. print hello_world()

When writing a fully featured test suite, though, you would probably want some more leverage Importing those macros by hand in every test can get stale rather quickly and leave behind a lot of boilerplate code.

Fortunately, this is Python. We have world class tools to combat repetition and verbosity, second only to Lisp macros. There is no reason we couldn’t write tests for our Jinja templates in clean and concise manner:

  1. class Hello(JinjaTestCase):
  2.     __template_imports__ = {
  3.         'hello_world': 'hello.html',
  4.     }
  5.  
  6.     def test_hello_world(self):
  7.         result = self.hello_world()
  8.         self.assert_in("hello", result.lower())
  9.         self.assert_in("world", result.lower())

The JinjaTestCase base, implemented in this gist, provides evidence that a little __metaclass__ can go a long way :)

Tags: , , , ,
Author: Xion, posted under Programming » Comments Off on Testing Jinja Templates

Making Flask’s url_for More Flexible

2013-03-11 0:32

Flask is one of the countless web frameworks available for Python. It’s probably my favorite, because it’s rather minimal, simple and easy to use. All the expected features are there, too, although they might not be as powerful as in some more advanced tools.

As an example, here’s how you define some simple request handler, bound to a parametrized URL pattern:

  1. from flask import abort, render_template
  2. from myapplication import app, db_session, Post
  3.  
  4. @app.route('/post/<int:post_id>')
  5. def blogpost(post_id):
  6.     post = db_session.query(Post).get(post_id)
  7.     if not post:
  8.         abort(404)
  9.     return render_template('post.html', post=post)

This handler responds to requests that go to /post/42 and similar paths. The syntax for those URL patterns is not very advanced: parameters can only be captured as path segments rather than arbitrary groups within a regular expression. (You can still use query string arguments, of course).

On the flip side, reversing the URL – building it from handler name and parameters – is always possible. There is a url_for function which does just that. It can be used both from Python code and, perhaps more usefully, from HTML (Jinja) templates:

  1. <h2>Recent posts</h2>
  2. <ul>
  3.   {% for post in posts %}
  4.     <li>
  5.       <a href="{{ url_for('blogpost', post_id=post.id) }}">
  6.         {{ post.title }}
  7.       </a>
  8.     </li>
  9.   {% endfor %}
  10. </ul>

Parameters can have types, too. We’ve seen, for example, that post_id was defined as int in the URL pattern for blogpost handler. These types are checked during the actual routing of HTTP requests, but also by the url_for function:

  1. pattern = url_for('blogpost', post_id='<id>')  # raises ValueError

Most of the time, this little bit of “static typing” is a nice feature. However, there are some cases where this behavior of url_for is a bit too strict. Anytime we don’t intend to invoke the resulting URL directly, we might want a little more flexibility.

Biggest case-in-point are various client-side templates, used by JavaScript code to update small pieces of HTML without reloading the whole page. If you, for example, wanted to rewrite the template above to use Underscore templates, you would still want url_for to format the blogpost URL pattern:

  1. <h2>Recent posts</h2>
  2. <ul>
  3.   < % _.each(posts, function(post) { %>
  4.     <a href="{{ url_for('blogpost', post_id='<%= post.id %>') }}">
  5.         < %- post.name %>
  6.     </a>
  7.   < % }); %>
  8. </ul>

Assuming you don’t feel dizzy from seeing two templating languages at once, you will obviously notice that '< %= post.id %>' is not a valid int value. But it’s a correct value for post_id parameter, because the resulting URL (/post/< %= post.id %>) would not be used immediately. Instead, it would be just sent to the browser, where some JS code would pick it up and replace the Underscore placeholder with an actual ID.

Unfortunately, bypassing the default strictness of url_for is not exactly easy.

Tags: , , , ,
Author: Xion, posted under Computer Science & IT » Comments Off on Making Flask’s url_for More Flexible
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.