So how to mix the two?
After discussing implementations with the Editor, we found this syntax agreeable:
h1. Contact Us
p. You can use this form to send us a message.
b(#form).
p. Thank you for you interest.
The magic here is the fake, Textile-like `b` tag, with an ID. This indicates to insert some sort of dynamic element in between the normal Textile content.
Why `b`? On the backend, I realized that I wanted to use the full power of Django templates for any of these dynamic elements. How do you segment off sections of content within a Django template? Blocks.
Now the trick is to render the template as normal, render the `form` block, then replace `b(#form).` with the rendered block.
Here is the view logic:
def render(request, section, template, context_vars={}):
"""
Renders a section with a template. Substitues any
textile-ish `b.` blocks with orphan blocks found in
said template.
"""
# resolve the template and render it to a string
content = render_to_string(template, context_vars)
# add a recursive flatten method to NodeList to make
# getting blocks easy
from django.template import NodeList
def flatten(self):
nodes = []
for node in self:
nodes.append(node)
if hasattr(node, 'nodelist'):
nodes.extend(node.nodelist.flatten())
return nodes
NodeList.flatten = flatten
# load the template
from django.template.loader import get_template
t = get_template(template)
# get all the block nodes, by flattening NodeList
from django.template.loader_tags import BlockNode
nodes = t.nodelist.flatten()
blocknodes = []
for node in nodes:
if node.__class__ is BlockNode:
blocknodes.append(node)
# render blocknodes with context into
# name => content dict
from django.template.context import Context
context = Context(context_vars)
blocks = {}
for blocknode in blocknodes:
blocks[blocknode.name] = blocknode.render(context)
# parse out textile-style block tags b(#block-name)
# and substitute rendered block
p = re.compile(r'(b\(#([^\)]+)\).)')
matches = p.findall(content)
for match in matches:
tag, name = match
try:
content = content.replace(tag, blocks[name])
except:
pass
return HttpResponse(content)
And here is the corresponding template:
{% extends 'layout/one-column.html' %}
{% comment %}
This node is an orphan, and would not be rendered
with a normal render_to_string. It is, however,
possible to render it with the same context, then
replace part of render_to_string and return the
resulting string.
{% endcomment %}
{% block form %}
<form action="" method="post">
<table>
{{ form }}
<tr><td colspan="2" align="right">
<input type="submit" name="submit" value="Submit" />
</td></tr>
</table>
</form>
{% endblock %}
1 comment:
Thanks buddy, this helped much!
Post a Comment