thirdparty/google_appengine/google/appengine/ext/webapp/template.py
changeset 109 620f9b141567
child 686 df109be0567c
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Copyright 2007 Google Inc.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #     http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 #
       
    17 
       
    18 """A simple wrapper for Django templates.
       
    19 
       
    20 The main purpose of this module is to hide all of the package import pain
       
    21 you normally have to go through to get Django to work. We expose the Django
       
    22 Template and Context classes from this module, handling the import nonsense
       
    23 on behalf of clients.
       
    24 
       
    25 Typical usage:
       
    26 
       
    27    from google.appengine.ext.webapp import template
       
    28    print template.render('templates/index.html', {'foo': 'bar'})
       
    29 
       
    30 Django uses a global setting for the directory in which it looks for templates.
       
    31 This is not natural in the context of the webapp module, so our load method
       
    32 takes in a complete template path, and we set these settings on the fly
       
    33 automatically.  Because we have to set and use a global setting on every
       
    34 method call, this module is not thread safe, though that is not an issue
       
    35 for applications.
       
    36 
       
    37 Django template documentation is available at:
       
    38 http://www.djangoproject.com/documentation/templates/
       
    39 """
       
    40 
       
    41 
       
    42 
       
    43 
       
    44 
       
    45 import md5
       
    46 import os
       
    47 
       
    48 try:
       
    49   from django import v0_96
       
    50 except ImportError:
       
    51   pass
       
    52 import django
       
    53 
       
    54 import django.conf
       
    55 try:
       
    56   django.conf.settings.configure(
       
    57     DEBUG=False,
       
    58     TEMPLATE_DEBUG=False,
       
    59     TEMPLATE_LOADERS=(
       
    60       'django.template.loaders.filesystem.load_template_source',
       
    61     ),
       
    62   )
       
    63 except (EnvironmentError, RuntimeError):
       
    64   pass
       
    65 import django.template
       
    66 import django.template.loader
       
    67 
       
    68 from google.appengine.ext import webapp
       
    69 
       
    70 def render(template_path, template_dict, debug=False):
       
    71   """Renders the template at the given path with the given dict of values.
       
    72 
       
    73   Example usage:
       
    74     render("templates/index.html", {"name": "Bret", "values": [1, 2, 3]})
       
    75 
       
    76   Args:
       
    77     template_path: path to a Django template
       
    78     template_dict: dictionary of values to apply to the template
       
    79   """
       
    80   t = load(template_path, debug)
       
    81   return t.render(Context(template_dict))
       
    82 
       
    83 
       
    84 template_cache = {}
       
    85 def load(path, debug=False):
       
    86   """Loads the Django template from the given path.
       
    87 
       
    88   It is better to use this function than to construct a Template using the
       
    89   class below because Django requires you to load the template with a method
       
    90   if you want imports and extends to work in the template.
       
    91   """
       
    92   abspath = os.path.abspath(path)
       
    93 
       
    94   if not debug:
       
    95     template = template_cache.get(abspath, None)
       
    96   else:
       
    97     template = None
       
    98 
       
    99   if not template:
       
   100     directory, file_name = os.path.split(abspath)
       
   101     new_settings = {
       
   102         'TEMPLATE_DIRS': (directory,),
       
   103         'TEMPLATE_DEBUG': debug,
       
   104         'DEBUG': debug,
       
   105         }
       
   106     old_settings = _swap_settings(new_settings)
       
   107     try:
       
   108       template = django.template.loader.get_template(file_name)
       
   109     finally:
       
   110       _swap_settings(old_settings)
       
   111 
       
   112     if not debug:
       
   113       template_cache[abspath] = template
       
   114 
       
   115     def wrap_render(context, orig_render=template.render):
       
   116       URLNode = django.template.defaulttags.URLNode
       
   117       save_urlnode_render = URLNode.render
       
   118       old_settings = _swap_settings(new_settings)
       
   119       try:
       
   120         URLNode.render = _urlnode_render_replacement
       
   121         return orig_render(context)
       
   122       finally:
       
   123         _swap_settings(old_settings)
       
   124         URLNode.render = save_urlnode_render
       
   125 
       
   126     template.render = wrap_render
       
   127 
       
   128   return template
       
   129 
       
   130 
       
   131 def _swap_settings(new):
       
   132   """Swap in selected Django settings, returning old settings.
       
   133 
       
   134   Example:
       
   135     save = _swap_settings({'X': 1, 'Y': 2})
       
   136     try:
       
   137       ...new settings for X and Y are in effect here...
       
   138     finally:
       
   139       _swap_settings(save)
       
   140 
       
   141   Args:
       
   142     new: A dict containing settings to change; the keys should
       
   143       be setting names and the values settings values.
       
   144 
       
   145   Returns:
       
   146     Another dict structured the same was as the argument containing
       
   147     the original settings.  Original settings that were not set at all
       
   148     are returned as None, and will be restored as None by the
       
   149     'finally' clause in the example above.  This shouldn't matter; we
       
   150     can't delete settings that are given as None, since None is also a
       
   151     legitimate value for some settings.  Creating a separate flag value
       
   152     for 'unset' settings seems overkill as there is no known use case.
       
   153   """
       
   154   settings = django.conf.settings
       
   155   old = {}
       
   156   for key, value in new.iteritems():
       
   157     old[key] = getattr(settings, key, None)
       
   158     setattr(settings, key, value)
       
   159   return old
       
   160 
       
   161 
       
   162 def create_template_register():
       
   163   """Used to extend the Django template library with custom filters and tags.
       
   164 
       
   165   To extend the template library with a custom filter module, create a Python
       
   166   module, and create a module-level variable named "register", and register
       
   167   all custom filters to it as described at
       
   168   http://www.djangoproject.com/documentation/templates_python/
       
   169     #extending-the-template-system:
       
   170 
       
   171     templatefilters.py
       
   172     ==================
       
   173     register = webapp.template.create_template_register()
       
   174 
       
   175     def cut(value, arg):
       
   176       return value.replace(arg, '')
       
   177     register.filter(cut)
       
   178 
       
   179   Then, register the custom template module with the register_template_module
       
   180   function below in your application module:
       
   181 
       
   182     myapp.py
       
   183     ========
       
   184     webapp.template.register_template_module('templatefilters')
       
   185   """
       
   186   return django.template.Library()
       
   187 
       
   188 
       
   189 def register_template_library(package_name):
       
   190   """Registers a template extension module to make it usable in templates.
       
   191 
       
   192   See the documentation for create_template_register for more information."""
       
   193   if not django.template.libraries.get(package_name, None):
       
   194     django.template.add_to_builtins(package_name)
       
   195 
       
   196 
       
   197 Template = django.template.Template
       
   198 Context = django.template.Context
       
   199 
       
   200 
       
   201 def _urlnode_render_replacement(self, context):
       
   202   """Replacement for django's {% url %} block.
       
   203 
       
   204   This version uses WSGIApplication's url mapping to create urls.
       
   205 
       
   206   Examples:
       
   207 
       
   208   <a href="{% url MyPageHandler "overview" %}">
       
   209   {% url MyPageHandler implicit_args=False %}
       
   210   {% url MyPageHandler "calendar" %}
       
   211   {% url MyPageHandler "jsmith","calendar" %}
       
   212   """
       
   213   args = [arg.resolve(context) for arg in self.args]
       
   214   try:
       
   215     app = webapp.WSGIApplication.active_instance
       
   216     handler = app.get_registered_handler_by_name(self.view_name)
       
   217     return handler.get_url(implicit_args=True, *args)
       
   218   except webapp.NoUrlFoundError:
       
   219     return ''