Using string templates to separate views from models and controllers is fine, but passing data from controllers to views is often tiresome. Using frame inspection can make things a lot more straightforward, saving you the hassle of explicitely passing each and every bit of data the template needs through boring lines of code like {'name':name}. Here is a sample with a fake templating system.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import inspect
test_template = '''Hello, %(first_name)s %(last_name)s'''
def call_template(template,context=None,**kw):
""" Calls a template and returns the generated content.
this is pretty much braindead, it's just meant to give you
the general idea.
"""
# efficiency is not the problem here
if context is not None:
d = dict(context)
d.update(kw)
else:
d = kw
return template%d
def call_contextual_template(template):
# this is the magic line
frame = inspect.currentframe().f_back
# again, we don't care about efficiency, it's not the point here
d = dict(frame.f_globals)
d.update(frame.f_locals)
return call_template(template,d)
def test_1():
first_name = "Foo" # imagine it is fetched from database
last_name = "Bar"
print call_template(test_template,{
"first_name":first_name,
"last_name":last_name,
}) # this is ugly !
def test_2():
first_name = "Foo" # imagine it is fetched from database
last_name = "Bar"
print call_template(test_template,
first_name=first_name,
last_name=last_name,
) # this is ugly !
def test_3():
first_name = "Foo" # imagine it is fetched from database
last_name = "Bar"
print call_contextual_template(test_template) # this is much better
if __name__=='__main__':
test_1()
test_2()
test_3()
|
Using frame inspection can save you a lot of boilerplate coding such as {"name":name} or name=name in the above code. This saves you from having to explicitely pass data from the controller to the view ; every local or global variable reachable from the controller code is available in the template.
Of course, this may pose security issues (the template could access unwanted data) or separation issues (it could be tempting for the template coder to call some code reachable from the controller). In my context, this is not really a problem because we have some discipline about this.
The concept of 'contextual template' (as opposed to traditional templates) helps remembering the 'magic bit' about the template, which extracts its parameters from the calling code.
Just use locals().
Of course, that should have been ...
Right you are... ... but with locals() the caller has to do the trick. Frame inspection saves the caller from writing locals(). locals() may be more portable to other implementations than CPython, though.
What if there are additional layers between the controller and the view? This would be much more typical (and in fact, looking for a solution to this problem is what led me here). If there's more than one layer, you'll need to search up the frame stack to find the controller rather than just assuming it called the view directly.