I think a template is comparable to a method.
It does IPO (input-processing-output):
In Python, a method has required and optional arguments (with default values).
Is there a way to define (I call it) API for Django-templates?
Use case: The customer can edit templates via a web interface. I want to tell the customer if changes to the template are valid or not.
I could render the template see if an error happens, but this does not cover this case:
The template should render the value "foo_needs_to_get_rendered".
In this case, validating the template by rendering (and discarding the result) does not show an error.
Related: I would like to show the customer who edits the template a help message. I want to list all available variables of the context. Example: "You can use these variables: {{foo}}, {{bar}}, {{blue}} ..."
The Django template starts by looking for a key in the Context object (which is a kind of dict). If the key is not there, KeyError is raised which is caught by the template engine.. and normally, the key would be rendered as blank.
So it seems to me that if you want to bypass this behavior you need to have a missing key raise something other than KeyError. Or, you can catch KeyError before the template engine does, and save that information before re-raising.
You could possibly do this by subclassing Context, this would take a little researching of the template engine code.. but could be a really great way to do it. But you could also just wrap your API in a special class that does this.. and put it in the context. The following code HAS NOT BEEN TESTED.. think of it as pseudocode which you could possibly use as a starting point...
# our own KeyError class that won't be caught by django
class APIKeyError(Exception):
pass
class API(object):
def __init__(self, context, exc_class=APIKeyError):
self.context = context
self.exc_class = exc_class
self.invalid_keys = set()
self.used_keys = set()
@property
def unused_keys(self):
return set(self.context.keys()) - self.used_keys
def __getattr__(self, name):
try:
value = self.context.get(name)
self.used_keys.add(name)
return value
except KeyError:
self.invalid_keys.add(name)
if self.exc_class is None:
raise
raise self.exc_class(
'API key "%s" is not valid. Available keys are %s.' %
(name, self.context.keys()))
So then you would check your template something like this:
from django.template.loader import render_to_string
api = API({
'foo': 'foovalue',
'bar': 'barvalue',
'baz': 'bazvalue',
}, None)
render_to_string('template.html', {'api': api }, request=request)
# now the API class instance knows something about the keys (only within
# the 'api' namespace that the template used...
print api.used_keys # set of keys that the template accessed that were valid
print api.invalid_keys # set of keys the template accessed that were invalid
print api.unused_keys # set of keys valid keys that were not used
Now, note that if you don't want to do any checks at the end but only have an exception thrown if the user uses an invalid key, just don't pass None as exc_class and it'll throw an APIKeyError when there's a bad api.key in the template.
So, hopefully this'll give you some ideas and a starting point for some code.. as I said this has not been tested at all.. think of it as pseudocode.
Of course, this only protects the keys that are within API.. any key that does not start with api would behave as normal for Django. But the advantage here is that you're working just with your own code and not as exposed to breakage from changes to future versions of Django.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With