Using Jinja2 with Django

本文介绍如何将Jinja2模板引擎与Django框架进行集成。通过创建自定义渲染函数,可以轻松地在Django视图中使用Jinja2渲染模板,并提供了请求对象和上下文变量的支持。

https://dbanck.svbtle.com/using-jinja2-with-django



In the last days I tried to get Jinja2
working with Django. Here I want to
explain a bit about the integration progress.

First we need to install django and jinja2, but I won’t explain it in
here, just use easy_install. After installing you can check if
everything works by importing django and jinja2 inside a python
shell.

If it works we need to create a django project and a example
application. (Go for the django
tutorial

if you don’t know how to do this).

Now we need to tell django to use jinja2 to render templates. The best
way to do this is creating a little lib with function to do this which
one can import into the views. So we create inside our project root a
directory called libs and there an empty __init__.py file and a file
for the jinja2 things. Lets call it jin.py.

Inside this file we want to create a render_to_response function for
jinja2 templates which can be imported from any view.

So here we go:

from django.http import HttpResponse
from django.conf import settings
from django.utils import translation
from django.core.urlresolvers import get_callable
from jinja2 import FileSystemLoader, Environment, PackageLoader, ChoiceLoader
from functools import wraps

loader_array = []
for pth in getattr(settings, 'TEMPLATE_DIRS', ()):
    loader_array.append(FileSystemLoader(pth))

for app in settings.INSTALLED_APPS:
    loader_array.append(PackageLoader(app))

default_mimetype = getattr(settings, 'DEFAULT_CONTENT_TYPE')
global_exts = getattr(settings, 'JINJA_EXTS', ())
env = Environment(extensions=global_exts, loader=ChoiceLoader(loader_array))

This creates the basic jinja2 environment. Notice that it automatically
reads the TEMPLATE_DIRS from settings.py and furthermore you can
declare inside the settings file which jinja2 extensions should be
loaded.

if 'jinja2.ext.i18n' in global_exts:
    env.install_gettext_translations(translation)

global_imports = getattr(settings, 'JINJA_GLOBALS', ())
for imp in global_imports:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.globals[method_name] = method
    else:
        env.globals[method.__name__] = method

global_filters = getattr(settings, 'JINJA_FILTERS', ())
for imp in global_filters:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.filters[method_name] = method
    else:
        env.filters[method.__name__] = method

global_tests = getattr(settings, 'JINJA_TESTS', ())
for imp in global_tests:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.tests[method_name] = method
    else:
        env.tests[method.__name__] = method

We can also declare the jinja2 globas, filters and tests inside the
settings.py via adding:

JINJA_EXTS = (
    'jinja2.ext.i18n',
)

JINJA_GLOBALS = (

)

JINJA_FILTERS = (

)

JINJA_TESTS = (

)

to it. So until now our jinja2 environment contains the i18n extension
and all other things added in settings.py. Time to create a
render_to_response function:

def render_to_string(filename, context={}):
    template = env.get_template(filename)
    rendered = template.render(**context)
    return rendered

def render_to_response(filename, context={}, request=None, mimetype=default_mimetype):
    if request:
        context['request'] = request
        context['user'] = request.user
    rendered = render_to_string(filename, context)
    return HttpResponse(rendered,mimetype=mimetype)

This function also adds the user object to the context if a request is
available. Inside a view we can to render a template with jinja2:

from libs.jin import render_to_response

def index(request):
    return render_to_response('app_name/index.html')

Wow. This wasn’t hard, was it? But you might notice that the user object
still isn’t available. To get the user object back we would need to pass
the request object every time to the render_to_response function.
That’s really annoying and looks ugly:

return render_to_response('app_name/index.html',{},request)

So we create a decorator with some more nice features inside our
jin.py.

def jin_renderer(prefix=None):
    def renderer(func):
        @wraps(func)
        def wrapper(request, *args, **kwargs):
            global default_mimetype
            response = func(request, *args, **kwargs)

            if isinstance(response, HttpResponse):
                return response
            elif isinstance(response, basestring):
                template_name = response
                context_processors = {}
                mimetype = default_mimetype
            elif isinstance(response, (tuple, list)):
                len_tuple = len(response)
                if len_tuple == 2:
                    template_name, context_processors = response
                    mimetype = default_mimetype
                elif len_tuple == 3:
                    template_name, context_processors, mimetype = response

            if prefix:
                if isinstance(template_name, (list, tuple)):
                    template_name = map(correct_path, template_name)
                else:
                    template_name = correct_path(template_name)

            return render_to_response(template_name,context_processors,request,mimetype)
        return wrapper

    def correct_path(template_name):
        if template_name.startswith('/'):
            return template_name[1:]
        return '%s%s' % (prefix, template_name)

    return renderer

When using this renderer the request object is always available and we
can also define a template prefix path. Using this new renderer is very
easy too:

from libs.jin import jin_renderer

render_to_html = jin_renderer('app_name/')

@render_to_html
def index(request):
    return 'index.html',{'test':'some context test'}

Now the index.html side your templates/app_name/ will be rendered.
These sites helped me creating the code:


""" Django settings for xiaoyu_mall project. Generated by 'django-admin startproject' using Django 2.2.3. For more information on this file, see https://docs.djangoproject.com/en/2.2/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os, sys # 确保日志目录存在 # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import django_redis.client BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '!zdqixv3&l91at7mhg_t9ukhuhursgvm2pg#%k6m8mp3#+x&fo' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'xiaoyu_mall.apps.users', ] # 指定自定义用户模型 AUTH_USER_MODEL = 'users.User' MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'xiaoyu_mall.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.jinja2.Jinja2', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'environment': 'xiaoyu_mall.utils.jinja2_env.jinja2_environment', } }, { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { #'environment': 'xiaoyu_mall.utils.jinja2_env.jinja2_environment', 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], } }, ] WSGI_APPLICATION = 'xiaoyu_mall.wsgi.application' # Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'ENGINE': 'django.db.backends.mysql', # 数据库引擎 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': '123456', # 数据库用户密码 'NAME': 'xiaoyu', # 数据库名字 } } # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, "session": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, "verify_code": { # 保存验证码 "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/2", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient" } } } SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "session" # Internationalization # https://docs.djangoproject.com/en/2.2/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] LOGGING = { 'version': 1, 'disable_existing_loggers': False, # 是否禁用已经存在的日志器 'formatters': { # 日志信息显示的格式 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' }, 'simple': { 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' }, }, 'filters':{ 'require_debug_true':{ '()':'django.utils.log.RequireDebugTrue', }, }, 'handlers':{ 'console':{ 'level':'INFO', 'filters':['require_debug_true'], 'class':'logging.StreamHandler', 'formatter':'simple' }, 'file':{ 'level':'INFO', 'class':'logging.handlers.RotatingFileHandler', 'filename':os.path.join(os.path.dirname(BASE_DIR),'logs/xiaoyu.log'),### 'maxBytes':300*1024*1024, 'backupCount':10, 'formatter':'verbose' }, }, 'loggers':{ 'django':{ 'handlers':['console','file'], 'propagate':True, 'level':'INFO', }, } }
最新发布
05-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值