Django DIY Part 1: Building Templates for jinja2

Introduction


In this post I would like to describe the creation of a small framework with a plug-in system like django. Using external components only. Jinja2 for templates, bottle for environment variables, pymongo will be used instead of ORM, and beaker will be engaged in sessions.
In the first part I want to tell you how to conveniently connect Jinja2 so that templates can be assembled from different folders (read plugins) and cache them.
Also in the next part I would like to tell how to connect gettext to templates and automate their translation.

Framework structure


It is assumed that our framework as a library can be in any directory most likely for ubuntu '/usr/local/lib/python2.6/dist-packages/', and wherever the project is, let’s say, for example, '/ home / user / worckspace / '. The project has index.wsgi for mod_wsgi or for uwsgi index_wsgi.py which shows the path to our library if it was manually copied somewhere.
The project has approximately the following structure:
/project/
	/__init__.py
	/app/
		/__init__.py
		/app1/
			/__init__.py
			/templ/
			/static/
			/routes.py
			/models.py
			/views.py
	/teml/
	/static/
	/index.py
	/routes.py
	/views.py
	/models.py
	/settings.py

Accordingly, in the / templ subdirectories there will be templates, in / static statics, in / app any number of applications (or components, as you like).
Accordingly, it is assumed that our library also has an app folder, an analogue of the djang contrib, in which components with their templates will also lie.
A folder will also be created in the silence project, for example, cache in it jinja2 will save the template cache.

Connection templates


So, all our connection will lie in the core.py file in the core package, which in turn lies in the root of the library. Import the necessary classes.
from jinja2 import Environment, PackageLoader, FunctionLoader, FileSystemBytecodeCache

Next, we determine the path for the templates, we will call this function when loading the templates.
Templates to choose from can have several extensions.

def get_app_root(app):
	"""Returns path to app or app name."""
	if isinstance(app, (str, )):
		__import__(app)
		app = sys.modules[app]
	return os.path.dirname(os.path.abspath(app.__file__))
templ_exts = ['', '.tpl', '.html', '.htm'] 
def split_templ_name (t):
	""" определение пути для загрузки шаблонов. Двоеточие выступает символом разделителя для указания в каком модуле будет лежать шаблон. Чтоб не было путаницы с одинаковыми именами. """
	if ':' in t:
		# тут составляем путь до шаблона конкретного модуля
		module, file = t.split(":", 1)
		module_path = os.path.join(get_app_root( module), "templ", "")
	else:
		# путь к шаблонам которые лежат в папке проекта.
		module = ' '
		module_path = os.sep + 'templ' + os.sep
		file = t
	return (module, module_path, file)

Actually loading the templates.
Here you can implement alternative template storage places, for example, by loading from the database
def union_templ(t, **p):
	(module, module_path, file) = split_templ_name (t)
	def load_template (base, module_path, file):
		path = base + module_path + file
		template = ' '
		for ext in templ_exts:
			filename = path+ext
			if os.path.exists(filename):
				with open(filename, "rb") as f:
					template = f.read()
				break;
		return template
	template = load_template (os.getcwd(), module_path, file);
	if not template:
		template = load_template( settings.lib_path, module_path, file);
	if not template:
		return 'Template not found %s' % t
	return template.decode('UTF-8')


Automatically create a folder for caching templates.
jcp = os.path.join(os.getcwd(), 'jinja_cache')
if not os.path.isdir(jcp): os.mkdir(jcp)


Creating an object that controls the cache.
bcc = FileSystemBytecodeCache(jcp, '%s.cache')


We hang the hook on jinja2, where we indicate the need for caching and pass the function that needs to be called to load the templates.

jinja = Environment(auto_reload=True, loader=FunctionLoader(union_templ), bytecode_cache  = bcc ) 


The function that directly performs the rendering of the template. Now she is not doing anything special, directly transferring control to jinja2, but we will come back here.
def render_templ(t, **p):
	template = jinja.get_template(t)
	return template.render(**p)


Import this feature to the global level.
__builtin__.templ = render_templ


Now thanks to this last line, it will be enough to call the templ () function in any file and pass it the name of the template in the arguments and what to display in it. For example:
return templ ('app.base: base', param = param) or return templ ('base', param = param), ':' means that the template is not in the project, but in the corresponding component in this case 'app .base '.

Summary


So, for starters, we have a small framework with a modular structure. On the basis of which we will move on.
This is still the first article and much could probably be written better, so I will be glad to all the comments and questions.

Continuation

Also popular now: