Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define API for Django-Templates?

I think a template is comparable to a method.

It does IPO (input-processing-output):

  1. it takes some input.
  2. it does some processing
  3. it outputs something. Most likely HTML.

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?

  • I want some arguments to be required.
  • I want some arguments to have defaults.

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}} ..."

like image 258
guettli Avatar asked Nov 24 '25 19:11

guettli


1 Answers

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.

like image 154
little_birdie Avatar answered Nov 26 '25 18:11

little_birdie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!