【问题标题】:Django view - load template from calling app's dir firstDjango 视图 - 首先从调用应用程序的目录加载模板
【发布时间】:2011-03-06 19:00:19
【问题描述】:

我尝试在我的 HTML 模板上保持某种一致的命名方案。 IE。 index.html 用于主页面,delete.html 用于删除页面等等。但是app_directories 加载器似乎总是从按字母顺序排列的应用程序中加载模板。

有什么方法可以始终首先检查调用应用的templates 目录中的匹配项吗?

相关设置在我的settings.py:

PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))

TEMPLATE_LOADERS = (
    'django.template.loaders.app_directories.load_template_source',
    'django.template.loaders.filesystem.load_template_source',
)
TEMPLATE_DIRS = (
    os.path.join(PROJECT_PATH, 'templates'),
)

我尝试更改TEMPLATE_LOADERS 的顺序,但没有成功。


按 Ashok 的要求进行编辑:

每个应用的目录结构:

templates/
    index.html
    add.html
    delete.html
    create.html
models.py
test.py
admin.py
views.py

在每个应用的views.py中:

def index(request):
    # code...
    return render_to_response('index.html', locals())

def add(request):
    # code...
    return render_to_response('add.html', locals())

def delete(request):
    # code...
    return render_to_response('delete.html', locals())

def update(request):
    # code...
    return render_to_response('update.html', locals())

【问题讨论】:

  • 您能否提供“模板”目录的结构以及如何加载/渲染模板的示例代码
  • app_directories 加载器听起来有点误导我。我会假设它会像您一样从正确的应用程序目录加载模板。毕竟,django 应用程序的意义不在于能够创建分离的切片吗?

标签: django templates


【解决方案1】:

原因是 app_directories 加载器本质上与将每个应用的模板文件夹添加到 TEMPLATE_DIRS 设置相同,例如喜欢

TEMPLATE_DIRS = (
    os.path.join(PROJECT_PATH, 'app1', 'templates'),
    os.path.join(PROJECT_PATH, 'app2', 'template'),
    ...
    os.path.join(PROJECT_PATH, 'templates'),
)

问题在于,正如您所提到的,index.html 将始终在 app1/templates/index.html 中找到,而不是在任何其他应用程序中。如果不修改 app_directories 加载器并使用内省或传递应用程序信息,则没有简单的解决方案可以神奇地修复此行为,这有点复杂。更简单的解决方案:

  • 保持 settings.py 原样
  • 在每个应用的模板文件夹中添加一个带有应用名称的子目录
  • 在“app1/index.html”或“app2/index.html”等视图中使用模板

举个更具体的例子:

project
    app1
        templates
            app1
                index.html
                add.html
                ...
        models.py
        views.py
        ...
    app2
        ...

然后在视图中:

def index(request):
    return render_to_response('app1/index.html', locals())

您甚至可以编写一个包装器来自动将应用程序名称添加到您的所有视图中,甚至可以扩展为使用自省,例如:

def render(template, data=None):
    return render_to_response(__name__.split(".")[-2] + '/' + template, data)

def index(request):
    return render('index.html', locals())

_____name_____.split(".")[-2] 假定文件在一个包中,所以它会变成例如'app1.views' 到 'app1' 以添加到模板名称之前。这还假设用户在不重命名模板目录中的文件夹的情况下永远不会重命名您的应用程序,这可能不是一个安全的假设,在这种情况下,只需硬编码模板目录中的文件夹名称即可。

【讨论】:

  • 这几乎是我从 CakePHP 中唯一想念的东西 :) 我希望他们有一天会添加它。
  • 这是一个解决 django 功能损坏的好方法(它是一个非常易于扩展的模板系统的折衷方案);但它仍然需要将“appname”文件夹添加到您的应用模板文件夹中,并且“appname”可能不是您想要在项目中使用的那个...
  • 这是一个不错且简单的解决方案,只要没有嵌套应用程序... ;)
  • 这种添加许多模板对于 Django 来说不再是惯用的了。目前您可以改用“'APP_DIRS':True”。它递归地将模板目录包含到主项目
  • @Stallman 我认为没关系。但就我个人而言,我更愿意将模板放到他们的相关应用程序中,以防止混淆。例如,如果我有 2 个应用程序,分别称为“first”和“second”以及“change_form.html”模板,我会将其放在“first/templates/admin/change_form.html”和“second/templates/admin/”中change_form.html'。但这当然取决于你。如果您想将它们收集到位于根目录的 1 个文件夹中,请执行此操作。在那种情况下,我会有'templates/first/admin/change_form.html'和'templates/second/admin/change_form.html'。我认为这两种解决方案都可以。
【解决方案2】:

我知道这是一个旧线程,但我做了一些可重用的东西,它允许更简单的命名空间。您可以将以下内容作为模板加载器加载。它将在appname/templates/index.html 中找到appname/index.html

此处提供要点:https://gist.github.com/871567

"""
Wrapper for loading templates from "templates" directories in INSTALLED_APPS
packages, prefixed by the appname for namespacing.

This loader finds `appname/templates/index.html` when looking for something
of the form `appname/index.html`.
"""

from django.template import TemplateDoesNotExist
from django.template.loaders.app_directories import app_template_dirs, Loader as BaseAppLoader

class Loader(BaseAppLoader):
    '''
    Modified AppDirecotry Template Loader that allows namespacing templates
    with the name of their app, without requiring an extra subdirectory
    in the form of `appname/templates/appname`.
    '''
    def load_template_source(self, template_name, template_dirs=None):
        try:
            app_name, template_path = template_name.split('/', 1)
        except ValueError:
            raise TemplateDoesNotExist(template_name)

        if not template_dirs:
            template_dirs = (d for d in app_template_dirs if
                    d.endswith('/%s/templates' % app_name))

        return iter(super(Loader, self).load_template_source(template_path,
                template_dirs))

【讨论】:

  • 它的工作方式与 Django 1.5.x 中的 `django.template.loaders.app_directories.Loader` 完全一样。而且问题还没有解决。
【解决方案3】:

app_loader 在您的应用程序中查找模板,以便它们在您的 INSTALLED_APPS 中指定。 (http://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types)。

我的建议是在模板文件的名称前加上应用名称以避免这些命名冲突。

例如,app1 的模板目录如下所示:

templates/
    app1_index.html
    app1_delete.html
    app1_add.html
    app1_create.html

【讨论】:

  • 谢谢克里斯!经过很长时间为我寻找答案后 - 该应用程序未在 INSTALLED_APPS 中列出,因此它仅在基本模板文件夹中查找,而不是在应用程序本地文件夹中查找。对我来说,这是因为我的 settings.py 和我的 local_settings.py 不同步,因为只有 settings.py 是版本控制的,在拉取和更新后它有一个新应用程序,但我的 local_settings.py 覆盖了它。跨度>
猜你喜欢
  • 2017-03-30
  • 2016-06-04
  • 2017-04-26
  • 1970-01-01
  • 2013-06-13
  • 2014-12-24
  • 1970-01-01
  • 2014-08-18
  • 2011-09-18
相关资源
最近更新 更多