![]() |
version seven.   http://demongin.org |
Django: Use Template Tags to Swap HTML Tags
A short how-to that describes writing a custom template filter in Django to replace one HTML tag with another.
Wednesday, 2010-02-03 | Django, On the Internet, Programming
| [I]n the world of global capital, distressed assets are still more valued than distressed people. |
| Naomi Klein |
So, the recent addition of demongin.org to the roster of contributors to the RSS aggregator planetdjango.org brought my attention to a bug that was bollixing up code blocks in my RSS feeds.
Basically, the "code" tag I use to make my syntax high-lighting work wasn't being recognized by RSS clients (and for good reason) and code blocks were collapsing into an ugly, illegible mess:

Naturally, I couldn't just just throw off some quick postgres-fu to search and replace those tags with pre tags because doing so would break my syntax highlighting. So I decided to write a short Template Filter (a kind of Template Tag to sub code tags for pre tags when generating the RSS XML.
demongin/blog/templatetags/rss_filters.py
The first file I generated was the file in which the Template Filter was going to live. I named it demongin/blog/templatetags/rss_filters.py and wrote it out like this:from django import template from django.template.defaultfilters import stringfilter import re register = template.Library() # # Helper functions used for match/replace actions # def code_repl(match_obj, tag, close=False): if close: return "<%s>;" % "/" + tag else: return "<%s>" % tag @stringfilter @register.filter(name='tag_to_other_tag') def tag_to_other_tag(s, tags): """ Takes a string, the search tag and the replacement tag. Returns a string. """ try: # split_contents() knows not to split quoted strings. in_tag, out_tag = tags.split(",") in_tag = in_tag.strip() out_tag = out_tag.strip() except ValueError: return s p_tag_open = re.compile("<%s\s?.*?>|<%s>)" % (in_tag, in_tag)) p_tag_close = re.compile("<%s>" % "/" + in_tag) s = re.sub(p_tag_open, code_repl(s, out_tag), s) s = re.sub(p_tag_close, code_repl(s, out_tag, close=True), s) return str(s)
- I opted not to use BeautifulSoup even though I'm very fond of that particular module. The reason I did this is because I realized that I could do what I needed to do with regular expressions in fewer lines of code. And even though I know that HTML tags are not regular expressions and using a parser is technically a "better" solution, I decided to go with the solution that was easier to write/understand.
- I also made the function extensible/scalable: the way I set it up, you can pass the conversion function an "in" tag and an "out" tag (or, if you prefer, a "find" tag and a "replace" tag). I did this so that I could swap out other HTML tags in the future, if necessary.
demongin/templates/feeds/django_description.html
The next file to edit was my existing template for the "description" portions of the RSS feed. When I was done making change, the file looked like this:{% load rss_filters %} {% autoescape off %} <i>{{ obj.subtitle }}</i><br /> {{ obj.body|tag_to_other_tag:"code, pre" }} {% endautoescape %}
Simple.
Once I was done there, I did the same thing for my primary feed and you can see the results in either feed by loading it into your favorite feed reader. http://demongin.org/feeds/latest or http://demongin.org/feeds/django now come out like this:

And that's word.
