def copy_dir(self, source, dest, level=0): '''Copies the static files from one directory to another. If this command is run, we assume the user wants to overwrite any existing files.''' # ensure the destination exists if not os.path.exists(dest): os.mkdir(dest) # go through the files in this directory for fname in os.listdir(source): source_path = os.path.join(source, fname) dest_path = os.path.join(dest, fname) ext = os.path.splitext(fname)[1].lower() ### EXPLICIT IGNORE ### if self.ignore_file(fname): pass ### DIRECTORIES ### # ignore these directories elif os.path.isdir(source_path) and fname in ( 'templates', 'views', get_setting('TEMPLATES_CACHE_DIR'), '__pycache__' ): pass # if a directory, create it in the destination and recurse elif os.path.isdir(source_path): if not os.path.exists(dest_path): os.mkdir(dest_path) elif not os.path.isdir(dest_path): # could be a file or link os.unlink(dest_path) os.mkdir(dest_path) self.copy_dir(source_path, dest_path, level+1) ### FILES ### # we don't do any regular files at the top level elif level == 0: pass # ignore these files elif fname in ( '__init__.py', ): pass # ignore these extensions elif ext in ( '.cssm', '.jsm' ): pass # if a regular Javscript file, minify it elif ext == '.js' and get_setting('MINIFY_JS_CSS', False) and JSMIN: fin = open(source_path) fout = open(dest_path, 'w') fout.write(jsmin(fin.read())) fout.close() fin.close() elif ext == '.css' and get_setting('MINIFY_JS_CSS', False) and CSSMIN: fin = open(source_path) fout = open(dest_path, 'w') fout.write(cssmin(fin.read())) fout.close() fin.close() # if we get here, it's a binary file like an image, movie, pdf, etc. else: shutil.copy2(source_path, dest_path)
def __init__(self, template, cgi_id=None): # set up the directories so we can go through them fast on render self.template_dir, self.template_name = os.path.split(os.path.splitext(template.filename)[0]) self.app_dir = os.path.dirname(self.template_dir) self.app = os.path.split(self.app_dir)[1] # ensure we have renderers for this template if self.app not in SCRIPT_RENDERERS: SCRIPT_RENDERERS[self.app] = MakoTemplateRenderer(self.app, 'scripts') if self.app not in STYLE_RENDERERS: STYLE_RENDERERS[self.app] = MakoTemplateRenderer(self.app, 'styles') # the static templatename.scss file try: scss_file = os.path.join(self.app_dir, 'styles', self.template_name + '.scss') scss_stat = os.stat(scss_file) try: css_file = os.path.join(self.app_dir, 'styles', self.template_name + '.css') css_stat = os.stat(css_file) # update the CSS file if the SCCS file is newer if scss_stat.st_mtime > css_stat.st_mtime: os.system(get_setting('SCCS_BINARY', '/usr/bin/env scss') + ' %s %s %s' % (get_setting('SCCS_ARGS', ''), scss_file, css_file)) except OSError: # if no CSS file exists, create one os.system(get_setting('SCCS_BINARY', '/usr/bin/env scss') + ' %s %s %s' % (get_setting('SCCS_ARGS', ''), scss_file, css_file)) except OSError: # no SCCS file exists .. continue as normal pass # the static templatename.css file try: fstat = os.stat(os.path.join(self.app_dir, 'styles', self.template_name + '.css')) self.css = '<link rel="stylesheet" type="text/css" href="%s?%s" />' % (os.path.join(settings.STATIC_URL, self.app, 'styles', self.template_name + '.css'), cgi_id if cgi_id != None else int(fstat.st_mtime)) except OSError: self.css = None # the mako-rendered templatename.cssm file try: fstat = os.stat(os.path.join(self.app_dir, 'styles', self.template_name + '.cssm')) self.cssm = self.template_name + '.cssm' except OSError: self.cssm = None # the static templatename.js file try: fstat = os.stat(os.path.join(self.app_dir, 'scripts', self.template_name + '.js')) self.js = '<script src="%s?%s"></script>' % (os.path.join(settings.STATIC_URL, self.app, 'scripts', self.template_name + '.js'), cgi_id if cgi_id != None else int(fstat.st_mtime)) except OSError: self.js = None # the mako-rendered templatename.jsm file try: fstat = os.stat(os.path.join(self.app_dir, 'scripts', self.template_name + '.jsm')) self.jsm = self.template_name + '.jsm' except OSError: self.jsm = None
def __init__(self, app_path, template_subdir='templates'): '''Creates a renderer to the given path (relateive to the project root where settings.STATIC_ROOT points to)''' project_path = os.path.normpath(settings.BASE_DIR) self.app_path = app_path template_dir = get_app_template_dir(app_path, template_subdir) # raises ImproperlyConfigured if error self.template_search_dirs = [ template_dir ] if get_setting('TEMPLATES_DIRS', False): self.template_search_dirs.extend(get_setting('TEMPLATES_DIRS')) self.template_search_dirs.append(settings.BASE_DIR) self.cache_root = os.path.abspath(os.path.join(project_path, app_path, get_setting('TEMPLATES_CACHE_DIR'), template_subdir)) self.tlookup = TemplateLookup(directories=self.template_search_dirs, imports=get_setting('DEFAULT_TEMPLATE_IMPORTS'), module_directory=self.cache_root, collection_size=2000, filesystem_checks=settings.DEBUG, input_encoding=get_setting('DEFAULT_TEMPLATE_ENCODING', 'utf-8')) # Use the default django engine (to get the list of context processors) self.engine = Engine.get_default()
def __init__(self, app_path, template_subdir='templates'): '''Creates a renderer to the given path (relateive to the project root where settings.STATIC_ROOT points to)''' project_path = os.path.normpath(settings.BASE_DIR) self.app_path = app_path template_dir = get_app_template_dir(app_path, template_subdir) # raises ImproperlyConfigured if error self.template_search_dirs = [ template_dir ] if get_setting('TEMPLATES_DIRS', False): self.template_search_dirs.extend(get_setting('TEMPLATES_DIRS')) self.template_search_dirs.append(settings.BASE_DIR) self.cache_root = os.path.abspath(os.path.join(project_path, app_path, get_setting('TEMPLATES_CACHE_DIR'), template_subdir)) self.tlookup = TemplateLookup(directories=self.template_search_dirs, imports=get_setting('DEFAULT_TEMPLATE_IMPORTS'), module_directory=self.cache_root, collection_size=2000, filesystem_checks=settings.DEBUG, input_encoding=get_setting('DEFAULT_TEMPLATE_ENCODING', 'utf-8')) # Use the default django engine (to get the list of context processors) self.engine = Engine.get_default()
def get_template_js(self, request, context): '''Retrieves the static and mako_rendered CSS''' ret = [] for template in self.template_chain: ti = getattr(template, DMP_ATTR_NAME) if ti.js: ret.append(ti.js) # the <script> was already created once in the constructor if ti.jsm: js_text = SCRIPT_RENDERERS[ti.app].render(request, ti.jsm, context.kwargs) if JSMIN and get_setting('MINIFY_JS_CSS', False): js_text = jsmin(js_text) ret.append('<script>%s</script>' % js_text) return '\n'.join(ret)
def get_template_css(self, request, context): '''Retrives the static and mako-rendered CSS''' ret = [] for template in reversed(self.template_chain): # reverse so lower CSS overrides higher CSS in the inheritance chain ti = getattr(template, DMP_ATTR_NAME) if ti.css: ret.append(ti.css) # the <link> was already created once in the constructor if ti.cssm: css_text = STYLE_RENDERERS[ti.app].render(request, ti.cssm, context.kwargs) if JSMIN and get_setting('MINIFY_JS_CSS', False): css_text = cssmin(css_text) ret.append('<style type="text/css">%s</style>' % css_text) return '\n'.join(ret)
def get_template_js(self, request, context): '''Retrieves the static and mako_rendered CSS''' ret = [] for template in self.template_chain: ti = getattr(template, DMP_ATTR_NAME) if ti.js: ret.append( ti.js ) # the <script> was already created once in the constructor if ti.jsm: js_text = SCRIPT_RENDERERS[ti.app].render( request, ti.jsm, context.kwargs) if JSMIN and get_setting('MINIFY_JS_CSS', False): js_text = jsmin(js_text) ret.append('<script>%s</script>' % js_text) return '\n'.join(ret)
def get_template_css(self, request, context): '''Retrives the static and mako-rendered CSS''' ret = [] for template in reversed( self.template_chain ): # reverse so lower CSS overrides higher CSS in the inheritance chain ti = getattr(template, DMP_ATTR_NAME) if ti.css: ret.append( ti.css ) # the <link> was already created once in the constructor if ti.cssm: css_text = STYLE_RENDERERS[ti.app].render( request, ti.cssm, context.kwargs) if JSMIN and get_setting('MINIFY_JS_CSS', False): css_text = cssmin(css_text) ret.append('<style type="text/css">%s</style>' % css_text) return '\n'.join(ret)
def render_to_response(self, request, template, params={}, def_name=None): '''Runs a template and returns an HttpRequest object to it. This method returns a django.http.Http404 exception if the template is not found. If the template raises a django_mako_plus.controller.RedirectException, the browser is redirected to the given page, and a new request from the browser restarts the entire DMP routing process. If the template raises a django_mako_plus.controller.InternalRedirectException, the entire DMP routing process is restarted internally (the browser doesn't see the redirect). The method throws two signals: 1. dmp_signal_pre_render_template: you can (optionally) return a new Mako Template object from a receiver to replace the normal template object that is used for the render operation. 2. dmp_signal_post_render_template: you can (optionally) return a string to replace the string from the normal template object render. @request The request context from Django. If this is None, any TEMPLATE_CONTEXT_PROCESSORS defined in your settings file will be ignored but the template will otherwise render fine. @template The template file path to render. This is relative to the app_path/controller_TEMPLATES_DIR/ directory. For example, to render app_path/templates/page1, set template="page1.html", assuming you have set up the variables as described in the documentation above. @params A dictionary of name=value variables to send to the template page. @def_name Limits output to a specific top-level Mako <%block> or <%def> section within the template. If the section is a <%def>, it must have no parameters. For example, def_name="foo" will call <%block name="foo"></%block> or <%def name="foo()"></def> within the template. ''' try: content_type = mimetypes.types_map.get(os.path.splitext(template)[1].lower(), 'text/html') content = self.render(request, template, params, def_name) return HttpResponse(content.encode(settings.DEFAULT_CHARSET), content_type='%s; charset=%s' % (content_type, settings.DEFAULT_CHARSET)) except TopLevelLookupException: # template file not found log.debug('DMP :: template "%s" not found in search path: %s.' % (template, self.template_search_dirs)) raise Http404() except RedirectException: # redirect to another page e = sys.exc_info()[1] # Py2.7 and Py3+ compliant log.debug('DMP :: view function %s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_function, e.redirect_to)) # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=e) # send the browser the redirect command return e.get_response(request)
def render_to_response(self, request, template, params={}, def_name=None): '''Runs a template and returns an HttpRequest object to it. This method returns a django.http.Http404 exception if the template is not found. If the template raises a django_mako_plus.controller.RedirectException, the browser is redirected to the given page, and a new request from the browser restarts the entire DMP routing process. If the template raises a django_mako_plus.controller.InternalRedirectException, the entire DMP routing process is restarted internally (the browser doesn't see the redirect). The method throws two signals: 1. dmp_signal_pre_render_template: you can (optionally) return a new Mako Template object from a receiver to replace the normal template object that is used for the render operation. 2. dmp_signal_post_render_template: you can (optionally) return a string to replace the string from the normal template object render. @request The request context from Django. If this is None, any TEMPLATE_CONTEXT_PROCESSORS defined in your settings file will be ignored but the template will otherwise render fine. @template The template file path to render. This is relative to the app_path/controller_TEMPLATES_DIR/ directory. For example, to render app_path/templates/page1, set template="page1.html", assuming you have set up the variables as described in the documentation above. @params A dictionary of name=value variables to send to the template page. @def_name Limits output to a specific top-level Mako <%block> or <%def> section within the template. If the section is a <%def>, it must have no parameters. For example, def_name="foo" will call <%block name="foo"></%block> or <%def name="foo()"></def> within the template. ''' try: content_type = mimetypes.types_map.get(os.path.splitext(template)[1].lower(), 'text/html') content = self.render(request, template, params, def_name) return HttpResponse(content.encode(settings.DEFAULT_CHARSET), content_type='%s; charset=%s' % (content_type, settings.DEFAULT_CHARSET)) except TopLevelLookupException: # template file not found log.debug('DMP :: template "%s" not found in search path: %s.' % (template, self.template_search_dirs)) raise Http404() except RedirectException: # redirect to another page e = sys.exc_info()[1] # Py2.7 and Py3+ compliant log.debug('DMP :: view function %s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_function, e.redirect_to)) # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=e) # send the browser the redirect command return e.get_response(request)
def route_request(request): '''The main router for all calls coming in to the system.''' # output the variables so the programmer can debug where this is routing log.debug('DMP :: processing: app=%s, page=%s, func=%s, urlparams=%s' % (request.dmp_router_app, request.dmp_router_page, request.dmp_router_function, request.urlparams)) # set the full function location request.dmp_router_module = '.'.join([ request.dmp_router_app, 'views', request.dmp_router_page ]) # first try going to the view function for this request # we look for a views/name.py file where name is the same name as the HTML file response = None while True: # enables the InternalRedirectExceptions to loop around full_module_filename = os.path.normpath(os.path.join(settings.BASE_DIR, request.dmp_router_module.replace('.', '/') + '.py')) try: # look for the module, and if not found go straight to template if not os.path.exists(full_module_filename): log.debug('DMP :: module %s not found; sending processing directly to template %s.html' % (request.dmp_router_module, request.dmp_router_page_full)) try: dmp_renderer = get_renderer(request.dmp_router_app) except ImproperlyConfigured as e: log.debug('DMP :: %s' % (e)) raise Http404 return dmp_renderer.render_to_response(request, '%s.html' % request.dmp_router_page_full) # find the function module_obj = import_module(request.dmp_router_module) if not hasattr(module_obj, request.dmp_router_function): log.debug('DMP :: view function/class %s not in module %s; returning 404 not found.' % (request.dmp_router_function, request.dmp_router_module)) raise Http404 func_obj = getattr(module_obj, request.dmp_router_function) # see if the func_obj is actually a class -- we might be doing class-based views here if isinstance(func_obj, type): request.dmp_router_class = request.dmp_router_function request.dmp_router_function = request.method.lower() if not hasattr(func_obj, request.dmp_router_function): log.debug('DMP :: view class %s.%s has no method named %s; returning 404 not found.' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) raise Http404 func_obj = getattr(func_obj(), request.dmp_router_function) # move to the class.get(), class.post(), etc. method # ensure it is decorated with @view_function - this is for security so only certain functions can be called if getattr(func_obj, 'dmp_view_function', False) != True: log.debug('DMP :: view function/class %s found successfully, but it is not decorated with @view_function; returning 404 not found. Note that if you have multiple decorators on a function, the @view_function decorator must be listed first.' % (request.dmp_router_function)) raise Http404 # send the pre-signal if get_setting('SIGNALS', False): for receiver, ret_response in signals.dmp_signal_pre_process_request.send(sender=sys.modules[__name__], request=request): if isinstance(ret_response, (HttpResponse, StreamingHttpResponse)): return ret_response # call view function if request.dmp_router_class == None: log.debug('DMP :: calling view function %s.%s' % (request.dmp_router_module, request.dmp_router_function)) else: log.debug('DMP :: calling class-based view function %s.%s.%s' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) response = func_obj(request) # send the post-signal if get_setting('SIGNALS', False): for receiver, ret_response in signals.dmp_signal_post_process_request.send(sender=sys.modules[__name__], request=request, response=response): if ret_response != None: response = ret_response # sets it to the last non-None in the signal receiver chain # if we didn't get a correct response back, send a 404 if not isinstance(response, (HttpResponse, StreamingHttpResponse)): if request.dmp_router_class == None: log.debug('DMP :: view function %s.%s failed to return an HttpResponse (or the post-signal overwrote it). Returning Http404.' % (request.dmp_router_module, request.dmp_router_function)) else: log.debug('DMP :: class-based view function %s.%s.%s failed to return an HttpResponse (or the post-signal overwrote it). Returning Http404.' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) raise Http404 # return the response return response except InternalRedirectException: ivr = sys.exc_info()[1] # Py2.7 and Py3+ compliant # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_internal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=ivr) # do the internal redirect request.dmp_router_module = ivr.redirect_module request.dmp_router_function = ivr.redirect_function full_module_filename = os.path.normpath(os.path.join(settings.BASE_DIR, request.dmp_router_module.replace('.', '/') + '.py')) log.debug('DMP :: received an InternalViewRedirect to %s -> %s' % (full_module_filename, request.dmp_router_function)) except RedirectException: # redirect to another page e = sys.exc_info()[1] # Py2.7 and Py3+ compliant if request.dmp_router_class == None: log.debug('DMP :: view function %s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_function, e.redirect_to)) else: log.debug('DMP :: class-based view function %s.%s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function, e.redirect_to)) # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=e) # send the browser the redirect command return e.get_response(request) # the code should never get here raise Exception("Django-Mako-Plus router error: The route_request() function should not have been able to get to this point. Please notify the owner of the DMP project. Thanks.")
from django.core.management.base import BaseCommand, CommandError from django.utils.importlib import import_module from django.conf import settings from django_mako_plus.controller import router, get_setting from optparse import make_option import os, os.path, shutil, fnmatch # import minification if requested if get_setting('MINIFY_JS_CSS', False): try: from rjsmin import jsmin JSMIN = True except ImportError: JSMIN = False try: from rcssmin import cssmin CSSMIN = True except ImportError: CSSMIN = False class Command(BaseCommand): args = '' help = 'Collects static files, such as media, scripts, and styles, to a common directory root. This is done to prepare for deployment.' can_import_settings = True option_list = BaseCommand.option_list + ( make_option( '--overwrite', action='store_true', dest='overwrite', default=False,
def copy_dir(self, source, dest, level=0): '''Copies the static files from one directory to another. If this command is run, we assume the user wants to overwrite any existing files.''' # ensure the destination exists if not os.path.exists(dest): os.mkdir(dest) # go through the files in this directory for fname in os.listdir(source): source_path = os.path.join(source, fname) dest_path = os.path.join(dest, fname) ext = os.path.splitext(fname)[1].lower() ### EXPLICIT IGNORE ### if self.ignore_file(fname): pass ### DIRECTORIES ### # ignore these directories elif os.path.isdir(source_path) and fname in ( 'templates', 'views', get_setting('TEMPLATES_CACHE_DIR'), '__pycache__'): pass # if a directory, create it in the destination and recurse elif os.path.isdir(source_path): if not os.path.exists(dest_path): os.mkdir(dest_path) elif not os.path.isdir(dest_path): # could be a file or link os.unlink(dest_path) os.mkdir(dest_path) self.copy_dir(source_path, dest_path, level + 1) ### FILES ### # we don't do any regular files at the top level elif level == 0: pass # ignore these files elif fname in ('__init__.py', ): pass # ignore these extensions elif ext in ('.cssm', '.jsm'): pass # if a regular Javscript file, minify it elif ext == '.js' and get_setting('MINIFY_JS_CSS', False) and JSMIN: fin = open(source_path) fout = open(dest_path, 'w') fout.write(jsmin(fin.read())) fout.close() fin.close() elif ext == '.css' and get_setting('MINIFY_JS_CSS', False) and CSSMIN: fin = open(source_path) fout = open(dest_path, 'w') fout.write(cssmin(fin.read())) fout.close() fin.close() # if we get here, it's a binary file like an image, movie, pdf, etc. else: shutil.copy2(source_path, dest_path)
from django.core.management.base import BaseCommand, CommandError from django.utils.importlib import import_module from django.conf import settings from django_mako_plus.controller import router, get_setting from optparse import make_option import os, os.path, shutil, fnmatch # import minification if requested if get_setting('MINIFY_JS_CSS', False): try: from rjsmin import jsmin JSMIN = True except ImportError: JSMIN = False try: from rcssmin import cssmin CSSMIN = True except ImportError: CSSMIN = False class Command(BaseCommand): args = '' help = 'Collects static files, such as media, scripts, and styles, to a common directory root. This is done to prepare for deployment.' can_import_settings = True option_list = BaseCommand.option_list + ( make_option( '--overwrite', action='store_true', dest='overwrite', default=False,
def render(self, request, template, params={}, def_name=None): '''Runs a template and returns a string. Normally, you probably want to call render_to_response instead because it gives a full HttpResponse or Http404. This method raises a mako.exceptions.TopLevelLookupException if the template is not found. The method throws two signals: 1. dmp_signal_pre_render_template: you can (optionally) return a new Mako Template object from a receiver to replace the normal template object that is used for the render operation. 2. dmp_signal_post_render_template: you can (optionally) return a string to replace the string from the normal template object render. @request The request context from Django. If this is None, 1) any TEMPLATE_CONTEXT_PROCESSORS defined in your settings file will be ignored and 2) DMP signals will not be sent, but the template will otherwise render fine. @template The template file path to render. This is relative to the app_path/controller_TEMPLATES_DIR/ directory. For example, to render app_path/templates/page1, set template="page1.html", assuming you have set up the variables as described in the documentation above. @params A dictionary of name=value variables to send to the template page. @def_name Limits output to a specific top-level Mako <%block> or <%def> section within the template. If the section is a <%def>, it must have no parameters. For example, def_name="foo" will call <%block name="foo"></%block> or <%def name="foo()"></def> within the template. The block/def must be defined in the exact template. DMP does not support calling defs from super-templates. ''' # must convert the request context to a real dict to use the ** below context_dict = {} context_dict['request'] = request context_dict['settings'] = settings try: context_dict['STATIC_URL'] = settings.STATIC_URL # this is used so much in templates, it's useful to have it always available except AttributeError: pass context = Context(params) if request == None else RequestContext(request, params) # Django's RequestContext automatically runs all the TEMPLATE_CONTEXT_PROCESSORS and populates with variables with context.bind_template(self): for d in context: context_dict.update(d) # get the template template_obj = self.get_template(template) # send the pre-render signal if get_setting('SIGNALS', False) and request != None: for receiver, ret_template_obj in signals.dmp_signal_pre_render_template.send(sender=self, request=request, context=context, template=template_obj): if ret_template_obj != None: # changes the template object to the received template_obj = ret_template_obj # do we need to limit down to a specific def? # this only finds within the exact template (won't go up the inheritance tree) # I wish I could make it do so, but can't figure this out render_obj = template_obj if def_name: # do we need to limit to just a def? render_obj = template_obj.get_def(def_name) # PRIMARY FUNCTION: render the template log.debug('DMP :: rendering template %s' % template_obj.filename) if settings.DEBUG: try: content = render_obj.render_unicode(**context_dict) except: content = html_error_template().render_unicode() else: # this is outside the above "try" loop because in non-DEBUG mode, we want to let the exception throw out of here (without having to re-raise it) content = render_obj.render_unicode(**context_dict) # send the post-render signal if get_setting('SIGNALS', False) and request != None: for receiver, ret_content in signals.dmp_signal_post_render_template.send(sender=self, request=request, context=context, template=template_obj, content=content): if ret_content != None: content = ret_content # sets it to the last non-None return in the signal receiver chain # return return content
def __init__(self, template, cgi_id=None): # set up the directories so we can go through them fast on render self.template_dir, self.template_name = os.path.split( os.path.splitext(template.filename)[0]) self.app_dir = os.path.dirname(self.template_dir) self.app = os.path.split(self.app_dir)[1] # ensure we have renderers for this template if self.app not in SCRIPT_RENDERERS: SCRIPT_RENDERERS[self.app] = MakoTemplateRenderer( self.app, 'scripts') if self.app not in STYLE_RENDERERS: STYLE_RENDERERS[self.app] = MakoTemplateRenderer( self.app, 'styles') # the static templatename.scss file try: scss_file = os.path.join(self.app_dir, 'styles', self.template_name + '.scss') scss_stat = os.stat(scss_file) try: css_file = os.path.join(self.app_dir, 'styles', self.template_name + '.css') css_stat = os.stat(css_file) # update the CSS file if the SCCS file is newer if scss_stat.st_mtime > css_stat.st_mtime: os.system( get_setting('SCCS_BINARY', '/usr/bin/env scss') + ' %s %s %s' % (get_setting('SCCS_ARGS', ''), scss_file, css_file)) except OSError: # if no CSS file exists, create one os.system( get_setting('SCCS_BINARY', '/usr/bin/env scss') + ' %s %s %s' % (get_setting('SCCS_ARGS', ''), scss_file, css_file)) except OSError: # no SCCS file exists .. continue as normal pass # the static templatename.css file try: fstat = os.stat( os.path.join(self.app_dir, 'styles', self.template_name + '.css')) self.css = '<link rel="stylesheet" type="text/css" href="%s?%s" />' % ( os.path.join(settings.STATIC_URL, self.app, 'styles', self.template_name + '.css'), cgi_id if cgi_id != None else int(fstat.st_mtime)) except OSError: self.css = None # the mako-rendered templatename.cssm file try: fstat = os.stat( os.path.join(self.app_dir, 'styles', self.template_name + '.cssm')) self.cssm = self.template_name + '.cssm' except OSError: self.cssm = None # the static templatename.js file try: fstat = os.stat( os.path.join(self.app_dir, 'scripts', self.template_name + '.js')) self.js = '<script src="%s?%s"></script>' % (os.path.join( settings.STATIC_URL, self.app, 'scripts', self.template_name + '.js'), cgi_id if cgi_id != None else int(fstat.st_mtime)) except OSError: self.js = None # the mako-rendered templatename.jsm file try: fstat = os.stat( os.path.join(self.app_dir, 'scripts', self.template_name + '.jsm')) self.jsm = self.template_name + '.jsm' except OSError: self.jsm = None
objects in a dict or other type of cache. This isn't done here to keep things simpler. ''' from django.conf import settings from django_mako_plus.controller.router import MakoTemplateRenderer from django_mako_plus.controller import get_setting import os, os.path, time DMP_ATTR_NAME = 'dmp_templateinfo' # used to attach TemplateInfo objects to Mako templates # Import minification if requested JSMIN = False CSSMIN = False if get_setting('MINIFY_JS_CSS', False) and not settings.DEBUG: try: from rjsmin import jsmin JSMIN = True except ImportError: pass try: from rcssmin import cssmin CSSMIN = True except ImportError: pass ####################################################################### ### A dict of template renderers for scripts and styles in our apps. ### These are created as needed in TemplateInfo below and cached here.
def process_request(self, request): '''Called for each browser request. This adds the following fields to the request object: request.dmp_router_app The Django application (such as "calculator"). request.dmp_router_page The view module (such as "calc" for calc.py). request.dmp_router_page_full The view module as specified in the URL, including the function name if specified. request.dmp_router_function The function within the view module to be called (usually "process_request"). request.dmp_router_module The module path in Python terms, such as calculator.views.calc. request.urlparams A list of the remaining url parts (see the calc.py example). This method is run as part of the middleware processing, so it runs long before the route_request() method at the top of this file. ''' # split the path path_parts = request.path[1:].split('/') # [1:] to remove the leading / # splice the list if the settings need it start_index = get_setting('URL_START_INDEX', 0) if start_index > 0: path_parts = path_parts[start_index:] # ensure that we have at least 2 path_parts to work with # by adding the default app and/or page as needed if len(path_parts) == 0: path_parts.append(get_setting('DEFAULT_APP', 'homepage')) path_parts.append(get_setting('DEFAULT_PAGE', 'index')) elif len(path_parts) == 1: # /app or /page if path_parts[0] in TEMPLATE_RENDERERS: # one of our apps specified, so insert the default page path_parts.append(get_setting('DEFAULT_PAGE', 'index')) else: # not one of our apps, so insert the app and assume path_parts[0] is a page in that app path_parts.insert(0, get_setting('DEFAULT_APP', 'homepage')) if not path_parts[1]: # was the page empty? path_parts[1] = get_setting('DEFAULT_PAGE', 'index') else: # at this point in the elif, we know len(path_parts) >= 2 if path_parts[0] not in TEMPLATE_RENDERERS: # the first part was not one of our apps, so insert the default app path_parts.insert(0, get_setting('DEFAULT_APP', 'homepage')) if not path_parts[1]: # is the page empty? path_parts[1] = get_setting('DEFAULT_PAGE', 'index') # set the app and page in the request request.dmp_router_app = path_parts[0] request.dmp_router_page = path_parts[1] request.dmp_router_page_full = path_parts[1] # might be different from dmp_router_page when split by '.' below # see if a function is specified with the page (the . separates a function name) du_pos = request.dmp_router_page.find('.') if du_pos >= 0: request.dmp_router_function = request.dmp_router_page[du_pos+1:] request.dmp_router_function = request.dmp_router_function.replace('.', '_') # python methods can't have dot, so replace with an underscore if in the name request.dmp_router_page = request.dmp_router_page[:du_pos] else: # the . not found, and the __ not found, so go to default function name request.dmp_router_function = 'process_request' # set the class to be None (it is set later if we find a class-based view) request.dmp_router_class = None # set up the urlparams with the reamining path parts # note that I'm not using unquote_plus because the + switches to a space *after* the question mark (in the regular parameters) # in the normal url, spaces have to be quoted with %20. Thanks Rosie for the tip. request.urlparams = URLParamList([ unquote(s) for s in path_parts[2:] ])
def render(self, request, template, params={}, def_name=None): '''Runs a template and returns a string. Normally, you probably want to call render_to_response instead because it gives a full HttpResponse or Http404. This method raises a mako.exceptions.TopLevelLookupException if the template is not found. The method throws two signals: 1. dmp_signal_pre_render_template: you can (optionally) return a new Mako Template object from a receiver to replace the normal template object that is used for the render operation. 2. dmp_signal_post_render_template: you can (optionally) return a string to replace the string from the normal template object render. @request The request context from Django. If this is None, 1) any TEMPLATE_CONTEXT_PROCESSORS defined in your settings file will be ignored and 2) DMP signals will not be sent, but the template will otherwise render fine. @template The template file path to render. This is relative to the app_path/controller_TEMPLATES_DIR/ directory. For example, to render app_path/templates/page1, set template="page1.html", assuming you have set up the variables as described in the documentation above. @params A dictionary of name=value variables to send to the template page. @def_name Limits output to a specific top-level Mako <%block> or <%def> section within the template. If the section is a <%def>, it must have no parameters. For example, def_name="foo" will call <%block name="foo"></%block> or <%def name="foo()"></def> within the template. The block/def must be defined in the exact template. DMP does not support calling defs from super-templates. ''' # must convert the request context to a real dict to use the ** below context_dict = {} context_dict['request'] = request context_dict['settings'] = settings try: context_dict['STATIC_URL'] = settings.STATIC_URL # this is used so much in templates, it's useful to have it always available except AttributeError: pass context = Context(params) if request == None else RequestContext(request, params) # Django's RequestContext automatically runs all the TEMPLATE_CONTEXT_PROCESSORS and populates with variables with context.bind_template(self): for d in context: context_dict.update(d) # get the template template_obj = self.get_template(template) # send the pre-render signal if get_setting('SIGNALS', False) and request != None: for receiver, ret_template_obj in signals.dmp_signal_pre_render_template.send(sender=self, request=request, context=context, template=template_obj): if ret_template_obj != None: # changes the template object to the received template_obj = ret_template_obj # do we need to limit down to a specific def? # this only finds within the exact template (won't go up the inheritance tree) # I wish I could make it do so, but can't figure this out render_obj = template_obj if def_name: # do we need to limit to just a def? render_obj = template_obj.get_def(def_name) # PRIMARY FUNCTION: render the template log.debug('DMP :: rendering template %s' % template_obj.filename) if settings.DEBUG: try: content = render_obj.render_unicode(**context_dict) except: content = html_error_template().render_unicode() else: # this is outside the above "try" loop because in non-DEBUG mode, we want to let the exception throw out of here (without having to re-raise it) content = render_obj.render_unicode(**context_dict) # send the post-render signal if get_setting('SIGNALS', False) and request != None: for receiver, ret_content in signals.dmp_signal_post_render_template.send(sender=self, request=request, context=context, template=template_obj, content=content): if ret_content != None: content = ret_content # sets it to the last non-None return in the signal receiver chain # return return content
Note that with this Django starter kit, we recreate the static renderer each time. At deployment, it would speed things up considerably to cache these StaticRenderer objects in a dict or other type of cache. This isn't done here to keep things simpler. ''' from django.conf import settings from django_mako_plus.controller.router import MakoTemplateRenderer from django_mako_plus.controller import get_setting import os, os.path, time DMP_ATTR_NAME = 'dmp_templateinfo' # used to attach TemplateInfo objects to Mako templates # Import minification if requested JSMIN = False CSSMIN = False if get_setting('MINIFY_JS_CSS', False) and not settings.DEBUG: try: from rjsmin import jsmin JSMIN = True except ImportError: pass try: from rcssmin import cssmin CSSMIN = True except ImportError: pass ####################################################################### ### A dict of template renderers for scripts and styles in our apps. ### These are created as needed in TemplateInfo below and cached here. ### One for each app is created in each dict.
def process_request(self, request): '''Called for each browser request. This adds the following fields to the request object: request.dmp_router_app The Django application (such as "calculator"). request.dmp_router_page The view module (such as "calc" for calc.py). request.dmp_router_page_full The view module as specified in the URL, including the function name if specified. request.dmp_router_function The function within the view module to be called (usually "process_request"). request.dmp_router_module The module path in Python terms, such as calculator.views.calc. request.urlparams A list of the remaining url parts (see the calc.py example). This method is run as part of the middleware processing, so it runs long before the route_request() method at the top of this file. ''' # split the path path_parts = request.path[1:].split('/') # [1:] to remove the leading / # splice the list if the settings need it start_index = get_setting('URL_START_INDEX', 0) if start_index > 0: path_parts = path_parts[start_index:] # ensure that we have at least 2 path_parts to work with # by adding the default app and/or page as needed if len(path_parts) == 0: path_parts.append(get_setting('DEFAULT_APP', 'homepage')) path_parts.append(get_setting('DEFAULT_PAGE', 'index')) elif len(path_parts) == 1: # /app or /page if path_parts[0] in TEMPLATE_RENDERERS: # one of our apps specified, so insert the default page path_parts.append(get_setting('DEFAULT_PAGE', 'index')) else: # not one of our apps, so insert the app and assume path_parts[0] is a page in that app path_parts.insert(0, get_setting('DEFAULT_APP', 'homepage')) if not path_parts[1]: # was the page empty? path_parts[1] = get_setting('DEFAULT_PAGE', 'index') else: # at this point in the elif, we know len(path_parts) >= 2 if path_parts[0] not in TEMPLATE_RENDERERS: # the first part was not one of our apps, so insert the default app path_parts.insert(0, get_setting('DEFAULT_APP', 'homepage')) if not path_parts[1]: # is the page empty? path_parts[1] = get_setting('DEFAULT_PAGE', 'index') # set the app and page in the request request.dmp_router_app = path_parts[0] request.dmp_router_page = path_parts[1] request.dmp_router_page_full = path_parts[1] # might be different from dmp_router_page when split by '.' below # see if a function is specified with the page (the . separates a function name) du_pos = request.dmp_router_page.find('.') if du_pos >= 0: request.dmp_router_function = request.dmp_router_page[du_pos+1:] request.dmp_router_function = request.dmp_router_function.replace('.', '_') # python methods can't have dot, so replace with an underscore if in the name request.dmp_router_page = request.dmp_router_page[:du_pos] else: # the . not found, and the __ not found, so go to default function name request.dmp_router_function = 'process_request' # set the class to be None (it is set later if we find a class-based view) request.dmp_router_class = None # set up the urlparams with the reamining path parts # note that I'm not using unquote_plus because the + switches to a space *after* the question mark (in the regular parameters) # in the normal url, spaces have to be quoted with %20. Thanks Rosie for the tip. request.urlparams = URLParamList([ unquote(s) for s in path_parts[2:] ])
def route_request(request): '''The main router for all calls coming in to the system.''' # output the variables so the programmer can debug where this is routing log.debug('DMP :: processing: app=%s, page=%s, func=%s, urlparams=%s' % (request.dmp_router_app, request.dmp_router_page, request.dmp_router_function, request.urlparams)) # set the full function location request.dmp_router_module = '.'.join([ request.dmp_router_app, 'views', request.dmp_router_page ]) # first try going to the view function for this request # we look for a views/name.py file where name is the same name as the HTML file response = None while True: # enables the InternalRedirectExceptions to loop around full_module_filename = os.path.normpath(os.path.join(settings.BASE_DIR, request.dmp_router_module.replace('.', '/') + '.py')) try: # look for the module, and if not found go straight to template if not os.path.exists(full_module_filename): log.debug('DMP :: module %s not found; sending processing directly to template %s.html' % (request.dmp_router_module, request.dmp_router_page_full)) try: dmp_renderer = get_renderer(request.dmp_router_app) except ImproperlyConfigured as e: log.debug('DMP :: %s' % (e)) raise Http404 return dmp_renderer.render_to_response(request, '%s.html' % request.dmp_router_page_full) # find the function module_obj = import_module(request.dmp_router_module) if not hasattr(module_obj, request.dmp_router_function): log.debug('DMP :: view function/class %s not in module %s; returning 404 not found.' % (request.dmp_router_function, request.dmp_router_module)) raise Http404 func_obj = getattr(module_obj, request.dmp_router_function) # see if the func_obj is actually a class -- we might be doing class-based views here if isinstance(func_obj, type): request.dmp_router_class = request.dmp_router_function request.dmp_router_function = request.method.lower() if not hasattr(func_obj, request.dmp_router_function): log.debug('DMP :: view class %s.%s has no method named %s; returning 404 not found.' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) raise Http404 func_obj = getattr(func_obj(), request.dmp_router_function) # move to the class.get(), class.post(), etc. method # ensure it is decorated with @view_function - this is for security so only certain functions can be called if getattr(func_obj, 'dmp_view_function', False) != True: log.debug('DMP :: view function/class %s found successfully, but it is not decorated with @view_function; returning 404 not found. Note that if you have multiple decorators on a function, the @view_function decorator must be listed first.' % (request.dmp_router_function)) raise Http404 # send the pre-signal if get_setting('SIGNALS', False): for receiver, ret_response in signals.dmp_signal_pre_process_request.send(sender=sys.modules[__name__], request=request): if isinstance(ret_response, (HttpResponse, StreamingHttpResponse)): return ret_response # call view function if request.dmp_router_class == None: log.debug('DMP :: calling view function %s.%s' % (request.dmp_router_module, request.dmp_router_function)) else: log.debug('DMP :: calling class-based view function %s.%s.%s' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) response = func_obj(request) # send the post-signal if get_setting('SIGNALS', False): for receiver, ret_response in signals.dmp_signal_post_process_request.send(sender=sys.modules[__name__], request=request, response=response): if ret_response != None: response = ret_response # sets it to the last non-None in the signal receiver chain # if we didn't get a correct response back, send a 404 if not isinstance(response, (HttpResponse, StreamingHttpResponse)): if request.dmp_router_class == None: log.debug('DMP :: view function %s.%s failed to return an HttpResponse (or the post-signal overwrote it). Returning Http404.' % (request.dmp_router_module, request.dmp_router_function)) else: log.debug('DMP :: class-based view function %s.%s.%s failed to return an HttpResponse (or the post-signal overwrote it). Returning Http404.' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function)) raise Http404 # return the response return response except InternalRedirectException: ivr = sys.exc_info()[1] # Py2.7 and Py3+ compliant # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_internal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=ivr) # do the internal redirect request.dmp_router_module = ivr.redirect_module request.dmp_router_function = ivr.redirect_function full_module_filename = os.path.normpath(os.path.join(settings.BASE_DIR, request.dmp_router_module.replace('.', '/') + '.py')) log.debug('DMP :: received an InternalViewRedirect to %s -> %s' % (full_module_filename, request.dmp_router_function)) except RedirectException: # redirect to another page e = sys.exc_info()[1] # Py2.7 and Py3+ compliant if request.dmp_router_class == None: log.debug('DMP :: view function %s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_function, e.redirect_to)) else: log.debug('DMP :: class-based view function %s.%s.%s redirected processing to %s' % (request.dmp_router_module, request.dmp_router_class, request.dmp_router_function, e.redirect_to)) # send the signal if get_setting('SIGNALS', False): signals.dmp_signal_redirect_exception.send(sender=sys.modules[__name__], request=request, exc=e) # send the browser the redirect command return e.get_response(request) # the code should never get here raise Exception("Django-Mako-Plus router error: The route_request() function should not have been able to get to this point. Please notify the owner of the DMP project. Thanks.")