Django中嵌套模板目录结构的原因是什么?

nvbavucw  于 2023-03-31  发布在  Go
关注(0)|答案(2)|浏览(82)

django模板文件夹需要创建一个以应用名称命名的子文件夹,然后包含模板文件。当python manage.py collectstatic可以在遍历所有目录时推断出这些信息时,为什么需要这样做?这似乎非常多余。

ozxc1zmp

ozxc1zmp1#

首先,Django并不需要这种特定的文件夹结构来让模板工作,它只是一种既定的模式。当然,它有一个基本原理,正如官方文档所指出的那样:
模板命名空间
现在我们可以直接把模板放在polls/templates中了(而不是创建另一个polls子目录),但这实际上是一个坏主意。Django将选择它找到的第一个名称匹配的模板,如果您在不同的应用程序中有一个同名的模板,Django将无法区分它们。我们需要能够将Django指向正确的模板,最简单的方法是给它们命名,也就是说,把这些模板放在另一个以应用程序本身命名的目录中。
具体情况可以参考this questionthat another
简而言之,通过遵循此模式,您可以将模板组织成2组:
1.与您的特定站点或项目相关的模板可以位于TEMPLATES['DIRS']设置所指向的目录中;
1.与特定应用程序相关的模板,如果您使应用程序可插入,则可以 * 按原样 * 提供,应该位于'./appname/templates/appname/'中(并且TEMPLATES['APP_DIRS']必须为True)。这样可以避免此文件夹内的文件与外部文件之间的名称冲突。

mefy6pfw

mefy6pfw2#

这是一个很长的答案。这个想法是真正解释Django在加载模板时是如何工作的。我甚至给你看了一些Django源代码来解释一些要点。
Django使用引擎来加载模板。这个答案适用于默认引擎DjangoTemplatesdjango.template.backends.django.DjangoTemplates
让我们回顾一下您的评论:
“django模板文件夹需要创建一个以应用程序名称命名的子文件夹,然后包含模板文件。”
不,Django不要求你在templates文件夹中创建一个以应用名称命名的子文件夹。这不是要求,* 这只是一个建议。*
但为什么呢?让我们一步步来。

1)Django在哪里查找模板文件?

Django会在很多地方搜索模板目录,这取决于你的模板加载设置。
settings.py文件中定义了两个位置。当配置TEMPLATES时,您有DIRSAPP_DIRS。如下所示:

TEMPLATES = [{
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR/"templates"],
        'APP_DIRS': True,
        ...
}]
  • DIRS

是Django查找模板源文件的目录列表...
这应该设置为包含模板目录完整路径的字符串列表...
你的模板可以去任何你想去的地方,只要目录和模板是可读的web服务器...
示例:

TEMPLATES = [{
        'DIRS': [
            '/home/html/templates/lawrence.com',
            '/home/html/templates/default',
        ],},]

所以Django没有必要的结构。你可以有你想要的目录,只要路径在这里列出并且可以被web服务器读取。一个常见的情况是当你想为所有应用程序都要使用的模板创建一个项目文件夹时:

'DIRS': [BASE_DIR/"templates"]
这告诉Django在位于基目录(根目录)的templates文件夹中查找文件。

  • APP_DIRS

告知引擎是否应在已安装的应用程序中查找模板…
APP_DIRSTrue时,DjangoTemplates引擎会在已安装应用程序的templates子目录中查找模板。
按照惯例,DjangoTemplates会在每个INSTALLED_APPS...
对于INSTALLED_APPS中的每个应用程序,加载器都会查找templates子目录。如果该目录存在,Django会在其中查找模板...
它是一个布尔值,TrueFalse。如果是True,它将在每个应用程序中查找templates子目录:

project/
  appname/
    templates/

**这是Django对你的唯一要求。**如果你想在每个应用中有模板,你必须在应用目录中有一个templates子文件夹。

如果你有这些应用程序:

INSTALLED_APPS = ['myproject.polls', 'myproject.music']

APP_DIRS: True Django将在这里查找模板。

/path/to/myproject/polls/templates/
/path/to/myproject/music/templates/

2)为什么人们建议在templates文件夹(在已安装的应用程序中)中使用另一个具有应用程序名称的文件夹?

这是一个组织你的代码并帮助Django找到你真正请求的文件(模板命名空间)的问题。
重要的是要记住:当模板引擎加载模板时,它会按照DIRS设置中定义的顺序检查模板目录。如果在多个目录中找到同名模板,则使用找到的第一个目录。
模板引擎检查DIRS目录后,将在APP_DIRS目录中查找模板。如果在多个应用程序目录中找到同名模板,则使用INSTALLED_APPS中列出的第一个应用程序中的模板。
让我们看看如果我们不做模板命名空间会发生什么。我们有以下文件夹:

INSTALLED_APPS = ['project.app_1', 'project.app_2']

project/
  app_1/
    templates/
      detail.html
  app_2/
    templates/
      detail.html

如果我在app_2视图中工作,并希望加载模板detail.html,我会有一个惊喜。而不是从app_2加载模板,我会从app_1加载模板。这是因为文件具有相同的名称,并且app_1INSTALLED_APPS中排在第一位。
为了避免这个问题,我们将应用程序的名称添加到模板文件夹中。

project/
  app_1/
    templates/
      app_1/
        detail.html
  app_2/
    templates/
      app_2/
        detail.html

要从视图中加载模板,我需要“app_2/detail.html”。

3)您可以在Django源代码中检查这一点。

转到django/django/template/loaders/app_directories.py,你会发现:

class Loader(FilesystemLoader):
    def get_dirs(self):
        return get_app_template_dirs("templates")

它调用get_app_template_dirs()并将“template”作为参数传递。
django/django/template/utils.py

@functools.lru_cache
def get_app_template_dirs(dirname):
    """
    Return an iterable of paths of directories to load app templates from.
    dirname is the name of the subdirectory containing templates inside installed applications.
    [NOTE: Remember that "templates" was passed as argument, the dirname]
    """

    template_dirs = [
        Path(app_config.path) / dirname
        for app_config in apps.get_app_configs()
        if app_config.path and (Path(app_config.path) / dirname).is_dir()
    ]
    # Immutable return value because it will be cached and shared by callers.
    return tuple(template_dirs)

使用Path(app_config.path)/dirname可以得到appname/templates/
对于每个安装的应用程序在这里找到for app_config in apps.get_app_configs().
如果目录存在if app_config.path and (Path(app_config.path) / dirname).is_dir()
template_dirs是已安装应用程序中具有模板文件夹的所有目录。

4)如果你正在使用基于类的视图(CBV),你可以得到一些功能。

您在Blog应用程序中有Post型号的列表视图。

class BlogListView(ListView):
    model = Post

在没有明确模板名称的情况下,Django会从对象(模型)的名称中推断出一个。在这个例子中:

blog/post_list.html

其结构为:
应用程序名称:博客
model:other
视图类型:列表

appname/<model_name>_<view_type>.html

正如你已经知道的,Django会在每个INSTALLED_APPS中查找“templates”子目录。
因此,如果APP_DIRS: True,则基于类的视图期望加载模板的完整路径为:

/path/to/project/blog/templates/blog/post_list.html

这是CBV的预定义要求,但如果您定义了template_name参数,则可以对其进行修改。

class BlogListView(ListView):
    model = Post
    template_name = "blog/the_super_list_of_posts.html"

您可以在源代码中检查这一点:
转到django/django/views/generic/list.py

class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
    """Mixin for responding with a template and list of objects."""

     template_name_suffix = "_list"
     
    def get_template_names(self):
    """
    Return a list of template names to be used for the request. Must return
    a list. May not be called if render_to_response is overridden.
    """
        try:
            names = super().get_template_names()
        except ImproperlyConfigured:
            # If template_name isn't specified, it's not a problem --
            # we just start with an empty list.
            names = []

    # If the list is a queryset, we'll invent a template name based on the
    # app and model name. This name gets put at the end of the template
    # name list so that user-supplied names override the automatically-
    # generated ones.
        if hasattr(self.object_list, "model"):
            opts = self.object_list.model._meta
            names.append(
                "%s/%s%s.html"
                % (opts.app_label, opts.model_name, 
self.template_name_suffix)
        )
        elif not names:
            raise ImproperlyConfigured(
                "%(cls)s requires either a 'template_name' attribute "
                "or a get_queryset() method that returns a QuerySet."
               % {
                    "cls": self.__class__.__name__,
                 }
        )
        return names

您已经:

names = super().get_template_names() # get's the appname/templates
...
names.append(
    "%s/%s%s.html"
    % (opts.app_label, opts.model_name, self.template_name_suffix)
)

其中:
opts.app_label:是应用程序的名称

型号名称:是模特的名字
self.template_name_suffix:是后缀,在本例中为“_list”
它们一起构成了CBV查找的默认模板名称:

app_label/templates/app_label/<model_name>_<template_name_suffix>.html

文件。

  1. https://docs.djangoproject.com/en/4.1/ref/templates/api/
  2. https://docs.djangoproject.com/en/4.1/topics/templates/
  3. https://docs.djangoproject.com/en/4.2/intro/tutorial03/#write-views-that-actually-do-something
  4. https://github.com/django/django/blob/main/django/template/loaders/app_directories.py
  5. https://github.com/django/django/blob/main/django/template/utils.py
  6. https://docs.djangoproject.com/en/4.1/topics/class-based-views/generic-display/

相关问题