Python – Define an API for Django templates?

Define an API for Django templates?… here is a solution to the problem.

Define an API for Django templates?

I think templates are equivalent to methods.

Indeed IPO (input-process-output):

  1. It requires some input.
  2. It does some processing
  3. It outputs something. Most likely HTML.

In Python, methods have required parameters and optional parameters (with default values).

Is there a way to define (what I call it) an API for Django templates?

  • I need some parameters.
  • I would like some parameters to have default values.

Use case: Customers can edit templates through the web interface. I want to tell the customer if the changes to the template are valid.

I can render the template to see if an error occurs, but that doesn’t include this :

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 want to display a help message to customers who edit templates. I want to list all available variables for the context. Example: “You can use these variables: {{foo}}, {{bar}}, {{blue}} …”

Solution

The Django template first looks up the key in the Context object, a kind of dictionary. If the key does not exist, a KeyError is thrown, which is caught by the template engine. Typically, keys are rendered blank.

So it seems to me that if you want to bypass this behavior, you need to have a missing key throw something other than KeyError. Alternatively, you can catch the KeyError before the template engine captures and save that information before rethrowing.

You can do this by subclassing the Context, which requires some research into the template engine code… But this can be a very good approach. But you can also wrap your API in a special class that does this… And put it in context. The following code has not been tested. Think of it as pseudocode, you can use it 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()))

Then you will check your template 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 just throw an exception when the user uses an invalid key, don’t take None as >exc_class When there is an incorrect api .key in the template, it throws an APIKeyError.

So hopefully this will give you some ideas and some code as a starting point. As I said, this has not been tested at all. Think of it as pseudocode.

Of course, this only protects the key in the API. Any key that doesn’t start with an api behaves normally in Django. But the advantage here is that you only use your own code and are not broken by changes in the future version of Django.

Related Problems and Solutions