django - 用不同的基础模板渲染任意URL
我希望能用不同的基础模板来渲染我项目中的某些视图,或者说所有视图。换句话说,对于网址 /some/view
,我想要有一个 /inline/some/view
,并且能渲染出相同的内容,但使用不同的基础模板。
我不想修改每个视图来接受不同的模板,因为我希望这个功能能在项目中的所有应用中都能使用,包括像 django.contrib.auth
这样的部分。
到目前为止,我在 urls.py 中做了:
url("^inline/(?P<uri>.*)", format.inline, name='inline'),
还有视图文件 format.py:
from django.core.urlresolvers import resolve
def inline(request, uri=''):
# get the view that would normally handle this request
view, args, kwargs = resolve('/' + uri)
# call the view
kwargs['request'] = request
template_response = view(*args, **kwargs)
# ...now what?
我不知道接下来该怎么做。我能在调用 view()
之前修改整个模板链,这样 template_response.render()
就能正常工作吗?
也许我这个思路完全不对,应该考虑用中间件的方式,但我还是希望这个功能能通过网址来触发,因为这样以后跟内容团队解释起来会简单很多。
更新
我成功实现了我想要的效果,但实现方式有点欠缺。以下是我做的事情:
- 把我想要内联的视图的模板复制到
templates/inline/
目录下 - 把
{% extends base.html %}
替换成{% extends inline/base.html %}
这样修改视图:
from django.core.urlresolvers import resolve def inline(request, uri=''): # get the view that would normally handle this request view, args, kwargs = resolve('/' + uri) # call the view kwargs['request'] = request template_response = view(*args, **kwargs) response.template_name = os.path.join('inline', response.template_name) return response
我不喜欢这个解决方案,因为这需要管理那些内联模板,每当项目中的应用发生变化时就得替换或更新它们。我还是希望能有一个更简洁的解决方案。
更新 2:解决方案
chris-wesseling 说得完全正确;一个自定义模板加载器正是我需要的。为了后续参考,这里是我的实现。
app/loaders.py:
from django.conf import settings
from django.template.loader import BaseLoader
from django.template.base import TemplateDoesNotExist
import os
class BaseTemplateOverrideLoader(BaseLoader):
"""
Load templates from a specified subdirectory in the current app's directory.
"""
subdir = 'templates'
def load_template_source(self, template_name, template_dirs=None):
template_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
self.subdir
)
try:
t = os.path.join(template_dir, template_name)
with open(t, 'rb') as fp:
return (fp.read().decode(settings.FILE_CHARSET), template_dir)
except IOError:
pass
raise TemplateDoesNotExist(template_name)
class InlineTemplateLoader(BaseTemplateOverrideLoader):
"""
Override the location of base.html for inline views.
"""
is_usable = True
subdir = 'templates/inline'
# ... other custom override classes here ....
app/views/inline.py:
from django.conf import settings
from django.core.urlresolvers import resolve
from django.template import loader
def default(request, slug=None):
view, args, kwargs = resolve('/' + slug)
old_loaders = settings.TEMPLATE_LOADERS
# Temporarily insert the inline template loader into TEMPLATE_LOADERS;
# we must also force BaseLoader to reload all templates loaders since
# they are cached at compile time.
settings.TEMPLATE_LOADERS = ('app.loaders.InlineTemplateLoader', ) + \
settings.TEMPLATE_LOADERS
loader.template_source_loaders = None
# now call the original view that handles this request
kwargs['request'] = request
response = view(*args, **kwargs)
response_string = response.render()
# restore the template loaders to their original condition
settings.TEMPLATE_LOADERS = old_loaders
loader.template_source_loaders = None
return response_string
app/templates/inline/base.html:
{% comment %}
inline/base.html
-- render just the main content without any styles etc,
for loading as inline content via ajax or whatever.
{% endcomment %}
{% block main %}{% endblock %}
1 个回答
1
你可以自己实现一个 模板加载器,然后把它放到你的 设置中的 TEMPLATE_LOADERS 里。你可以看看这个 类似的问题,里面有一些你想做的事情的思路。
简单来说,你想要的是一种方法,可以从不同的地方加载 base.html
文件。