我可以从 Django 中的模板访问 settings.py 中的常量吗?

Posted

技术标签:

【中文标题】我可以从 Django 中的模板访问 settings.py 中的常量吗?【英文标题】:Can I access constants in settings.py from templates in Django? 【发布时间】:2012-09-27 19:36:23 【问题描述】:

我希望能够从模板访问 settings.py 中的一些内容,但我不知道该怎么做。我已经试过了

CONSTANT_NAME

但这似乎不起作用。这可能吗?

【问题讨论】:

如果您正在寻找如何将设置传递给每个答案,请查看 bchunn 关于上下文处理器的答案 @jkbrzt 的回答是一个预打包的解决方案,可以快速轻松地解决这个问题。未来的读者应该看看这个***.com/a/25841039/396005而不是接受的答案 【参考方案1】:

对于那些想要使用@Berislav 的方法(自定义模板标签)和if 标签的人:

/app/templatetags/my_settings.py:

from django import template
from django.conf import settings

register = template.Library()

@register.simple_tag
def settings_value(name):
    return getattr(settings, name, "")

模板文件:

<!-- Load your tags -->
% load my_settings %
% settings_value 'ENABLE_FEATURE_A' as ENABLE_FEATURE_A %

% if ENABLE_FEATURE_A %
<!-- Feature A stuffs -->
% endif %

【讨论】:

【参考方案2】:

我发现最简单的方法是单个custom template tag:

from django import template
from django.conf import settings

register = template.Library()

# settings value
@register.simple_tag
def settings_value(name):
    return getattr(settings, name, "")

用法:

% settings_value "LANGUAGE_CODE" %

【讨论】:

我喜欢按需访问模板中的任何设置,这很优雅。如果您经常在模板中使用各种设置,这确实比其他答案好得多:1)接受的答案与基于类的视图不兼容或笨拙。 2)使用过度投票的模板上下文处理器解决方案,您必须指定单个设置(或全部),并且它将针对呈现模板的每个请求运行 - 效率低下! 3)它比上面更复杂的标签更简单。 @BenRoberts 我确实同意这是一个优雅的解决方案……但仅适用于由单个开发人员完成所有工作的小型项目。如果您有单独的人员/团队进行设计和开发,那么这个解决方案可能是最糟糕的。什么可以阻止设计师滥用这个标签:% settings_value "DATABASES" %?这个用例应该清楚为什么设置在模板中不可用。 “我们这里都是成年人” 请原谅我是个新手。你把这段代码放在哪里?视图.py?还是在新文件上? 为了让其他人清楚,您需要: 1) 在您的应用程序内创建一个 templatetags 文件夹,其中包含一个空的 __init__.py 文件,该文件夹内的代码为 settings.py。 2)在您的模板中添加% load settings %,然后使用您的新标签!【参考方案3】:

将此代码添加到名为context_processors.py的文件中:

from django.conf import settings as django_settings


def settings(request):
    return 
        'settings': django_settings,
    

然后,在您的设置文件中,在TEMPLATES'context_processors' 设置中包含一个路径,例如'speedy.core.base.context_processors.settings'(带有您的应用名称和路径)。

(您可以看到例如settings/base.py 和context_processors.py)。

然后您可以在任何模板代码中使用特定设置。例如:

% if settings.SITE_ID == settings.SPEEDY_MATCH_SITE_ID %

更新:上面的代码向模板公开了所有设置,包括敏感信息,例如您的SECRET_KEY。黑客可能会滥用此功能在模板中显示此类信息。如果您只想向模板公开特定设置,请改用以下代码:

def settings(request):
    settings_in_templates = 
    for attr in ["SITE_ID", ...]: # Write here the settings you want to expose to the templates.
        if (hasattr(django_settings, attr)):
            settings_in_templates[attr] = getattr(django_settings, attr)
    return 
        'settings': settings_in_templates,
    

【讨论】:

我昨天遇到了这个问题,找到了这篇文章,然后找到了另外 2 篇文章和一篇博文,感觉每一篇都过于复杂(不幸的是,我没有把它放在页面的下方,真丢脸)。所以我最终推出了自己的解决方案,这正是这个解决方案。我刚回来是因为当这个 ^^^ 3 行函数和 settings.py 中的 1 行更改时,人们推荐插件和一大堆代码让我很烦恼。 @DXM 谢谢! 实际上我的解决方案将所有设置暴露给模板,包括敏感信息,例如SECRET_KEY。黑客可能会滥用此功能在模板中显示此类信息。 嗯...很好,现在我的网站也有同样的问题 :) 但是...我可能遗漏了一些东西,但是,我们确定有问题吗?模板本质上与您网站的源代码相同,不是吗?它们存储在服务器端,无法直接从前端访问。如果黑客可以更改模板,那么他们就可以更改任何 .py 文件。 这是迄今为止最简单、最干净的解决方案,谢谢。我只想添加一个小提示,可能会为未来的读者节省时间:您确实不必必须安装 speedy.net 或任何其他外部应用程序才能使此解决方案正常工作。只需将您的 context_processors.py 文件放在主应用程序的目录中(例如“myapp/”),然后将以下行添加到 settings.pyTEMPLATES.OPTIONS.context_processors 部分:@987654333 @ 或更多一般 '[APP_NAME].context_processors.settings' 其中 [APP_NAME] 应替换为您的应用程序的名称。【参考方案4】:

使用 Django 2.0+ 添加包含创建自定义模板标签的完整说明的答案

在您的应用文件夹中,创建一个名为 templatetags 的文件夹。在其中创建 __init__.pycustom_tags.py

custom_tags.py 中创建一个自定义标签函数,该函数提供对 settings 常量中任意键的访问:

from django import template
from django.conf import settings

register = template.Library()

@register.simple_tag
def get_setting(name):
    return getattr(settings, name, "")

要理解这段代码,我建议阅读 Django 文档中的 the section on simple tags。

然后,您需要将此文件加载到您将使用它的任何模板中,以使 Django 了解此(以及任何其他)自定义标签。就像你需要加载内置的静态标签一样:

% load custom_tags %

加载后,它可以像任何其他标签一样使用,只需提供您需要返回的特定设置。因此,如果您的设置中有 BUILD_VERSION 变量:

% get_setting "BUILD_VERSION" %

此解决方案不适用于数组,但如果您需要,您可能会在模板中添加大量逻辑。

注意:更简洁和故障安全的解决方案可能是创建一个自定义上下文处理器,您可以在其中将所需的设置添加到所有模板都可用的上下文中。这样可以降低在模板中错误地输出敏感设置的风险。

【讨论】:

【参考方案5】:

如果有人像我一样发现这个问题,那么我将发布适用于 Django 2.0 的解决方案:

此标签将一些 settings.py 变量值分配给模板的变量:

用法:% get_settings_value template_var "SETTINGS_VAR" %

app/templatetags/my_custom_tags.py:

from django import template
from django.conf import settings

register = template.Library()

class AssignNode(template.Node):
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def render(self, context):
        context[self.name] = getattr(settings, self.value.resolve(context, True), "")
        return ''

@register.tag('get_settings_value')
def do_assign(parser, token):
    bits = token.split_contents()
    if len(bits) != 3:
        raise template.TemplateSyntaxError("'%s' tag takes two arguments" % bits[0])
    value = parser.compile_filter(bits[2])
    return AssignNode(bits[1], value)

您的模板:

% load my_custom_tags %

# Set local template variable:
% get_settings_value settings_debug "DEBUG" %

# Output settings_debug variable:
 settings_debug 

# Use variable in if statement:
% if settings_debug %
... do something ...
% else %
... do other stuff ...
% endif %

在此处查看 Django 的文档如何创建自定义模板标签:https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/

【讨论】:

% if settings_debug % 谢谢@user66081!将% if settings_debug == True % 更改为您建议的% if settings_debug %【参考方案6】:

我喜欢 Berislav 的解决方案,因为在简单的网站上,它既干净又有效。我不喜欢随意公开所有设置常量。所以我最终做的是:

from django import template
from django.conf import settings

register = template.Library()

ALLOWABLE_VALUES = ("CONSTANT_NAME_1", "CONSTANT_NAME_2",)

# settings value
@register.simple_tag
def settings_value(name):
    if name in ALLOWABLE_VALUES:
        return getattr(settings, name, '')
    return ''

用法:

% settings_value "CONSTANT_NAME_1" %

这可以保护您在模板中使用的任何未命名的常量,如果您想要真正花哨,您可以在设置中设置一个元组,并为不同的页面、应用程序或区域创建多个模板标签,并根据需要简单地将本地元组与设置元组组合,然后执行列表推导以查看该值是否可以接受。 我同意,在一个复杂的网站上,这有点简单,但是有些值可以很好地普遍存在于模板中,这似乎很好用。 感谢 Berislav 的原创创意!

【讨论】:

为什么不简单地if name in ALLOWABLE_VALUES: ... 因为我认为我很聪明,并且想防止子字符串触发设置 var。 ;-) 返回应该是:return getattr(settings, is_allowable, '') 只是想澄清一下:'val' in ('val_first', 'second_val',)False,这里没有子字符串问题。 我如何在if 声明中使用它?我想检查DEBUG 如果有人需要重新包含的版本gist.github.com/BrnoPCmaniak/632f56ddb907108b3d43fa862510dfca【参考方案7】:

如果您希望每个请求和模板都具有该值,则使用 context processor 更合适。

方法如下:

    在您的应用目录中创建一个context_processors.py 文件。假设我想在每个上下文中都有 ADMIN_PREFIX_VALUE 值:

    from django.conf import settings # import the settings file
    
    def admin_media(request):
        # return the value you want as a dictionnary. you may add multiple values in there.
        return 'ADMIN_MEDIA_URL': settings.ADMIN_MEDIA_PREFIX
    

    将您的上下文处理器添加到您的 settings.py 文件中:

    TEMPLATES = [
        # whatever comes before
        'OPTIONS': 
            'context_processors': [
                # whatever comes before
                "your_app.context_processors.admin_media",
            ],
        
    ]
    

    在您的视图中使用RequestContext 在模板中添加上下文处理器。 render shortcut 会自动执行此操作:

    from django.shortcuts import render
    
    def my_view(request):
        return render(request, "index.html")
    

    最后,在您的模板中:

    ...
    <a href=" ADMIN_MEDIA_URL ">path to admin media</a>
    ...
    

【讨论】:

@MarkEssel 制作了这些箍,因此只要变量使用 RequestContext 函数,就可以在您创建的每个视图中访问该变量。您始终可以在每个视图中手动获取设置变量。我会随时选择可重用的上下文处理器,而不是好的复制粘贴。 尽我所能避免到处复制/粘贴。每个应用程序(在一个项目中)是否都需要一个 context_processor.py,有没有办法为所有应用程序构建一个 context_processor? @bchhun 我刚刚测试过(Django 1.3):在应用程序之间共享上下文处理器工作得很好。 :-) 我将context_process.py 放在我的settings.py 文件旁边,并将"context_processors.admin_media" 添加到我的TEMPLATE_CONTEXT_PROCESSORS 列表中。此外,您可能希望在答案中添加一条注释,说明 TEMPLATE_CONTEXT_PROCESSORS 的默认值不为空,因此如果任何现有代码使用这些默认上下文处理器设置的任何值,除非您将它们添加回来,否则它们将不起作用明确地添加到列表中。 @MarkEssel 一点都不痛苦——他只是把所有的东西都拼出来了。它实际上只有 6 条短线(第 1 步和第 2 步)。无论如何,大多数模板都需要步骤 3 和 4 或等效步骤。 从 Django 1.3 开始,您可以使用 render 快捷方式来避免必须显式包含 RequestContext:docs.djangoproject.com/en/1.6/topics/http/shortcuts/#render【参考方案8】:

如果我们要比较单个变量上的上下文和模板标签,那么了解更有效的选项可能是有益的。但是,您最好只从需要该变量的模板中深入了解设置。在这种情况下,将变量传递给所有模板是没有意义的。但是,如果您将变量发送到一个通用模板中,例如 base.html 模板,那么 base.html 模板在每个请求上都会呈现,所以您可以使用任何一种方法。

如果您决定使用模板标签选项,请使用以下代码,因为它允许您传递 default 值,以防问题中的变量未定义。 p>

示例:get_from_settings my_variable as my_context_value

示例:get_from_settings my_variable my_default as my_context_value

class SettingsAttrNode(Node):
    def __init__(self, variable, default, as_value):
        self.variable = getattr(settings, variable, default)
        self.cxtname = as_value

    def render(self, context):
        context[self.cxtname] = self.variable
        return ''


def get_from_setting(parser, token):
    as_value = variable = default = ''
    bits = token.contents.split()
    if len(bits) == 4 and bits[2] == 'as':
        variable = bits[1]
        as_value = bits[3]
    elif len(bits) == 5 and bits[3] == 'as':
        variable     = bits[1]
        default  = bits[2]
        as_value = bits[4]
    else:
        raise TemplateSyntaxError, "usage: get_from_settings variable default as value " \
                "OR: get_from_settings variable as value"

    return SettingsAttrNode(variable=variable, default=default, as_value=as_value)

get_from_setting = register.tag(get_from_setting)

【讨论】:

或者你可以使用SITE_EXTRA_CONTEXT_DICT in finalware 为你做。【参考方案9】:

查看django-settings-export(免责声明:我是这个项目的作者)。

例如...

$ pip install django-settings-export

settings.py

TEMPLATES = [
    
        'OPTIONS': 
            'context_processors': [
                'django_settings_export.settings_export',
            ],
        ,
    ,
]

MY_CHEESE = 'Camembert';

SETTINGS_EXPORT = [
    'MY_CHEESE',
]

模板.html

<script>var MY_CHEESE = ' settings.MY_CHEESE ';</script>

【讨论】:

请注意,在您的视图中,您需要使用render 而不是render_to_response 我有类似的要求从模板中的设置中读取值,但是当我在设置文件中添加 'django_settings_export.settings_export' 时出现 500 错误。你能建议我在这里做错了什么 现在是 2019 年,我正在我的项目中使用它。谢谢! 我同意@sivabudh。这对我来说也是最好的解决方案,因为 1. 它是集中的,这意味着我不需要额外的文件夹和文件, 2. 我可以在模板中看到设置命名空间,这对于获取许多应用程序的引用非常有帮助。 【参考方案10】:

我发现这是 Django 1.3 最简单​​的方法:

    views.py

    from local_settings import BASE_URL
    
    def root(request):
        return render_to_response('hero.html', 'BASE_URL': BASE_URL)
    

    hero.html

    var BASE_URL = ' JS_BASE_URL ';
    

【讨论】:

【参考方案11】:

如果使用基于类的视图:

#
# in settings.py
#
YOUR_CUSTOM_SETTING = 'some value'

#
# in views.py
#
from django.conf import settings #for getting settings vars

class YourView(DetailView): #assuming DetailView; whatever though

    # ...

    def get_context_data(self, **kwargs):

        context = super(YourView, self).get_context_data(**kwargs)
        context['YOUR_CUSTOM_SETTING'] = settings.YOUR_CUSTOM_SETTING

        return context

#
# in your_template.html, reference the setting like any other context variable
#
 YOUR_CUSTOM_SETTING 

【讨论】:

【参考方案12】:

IanSR 和 bchhun 都建议在设置中覆盖 TEMPLATE_CONTEXT_PROCESSORS。请注意,此设置有一个默认值,如果您在不重新设置默认值的情况下覆盖它,可能会导致一些麻烦的事情。在最新版本的 Django 中,默认值也发生了变化。

https://docs.djangoproject.com/en/1.3/ref/settings/#template-context-processors

默认的 TEMPLATE_CONTEXT_PROCESSORS :

TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")

【讨论】:

【参考方案13】:

我稍微改进了chrisdew's answer(以创建您自己的标签)。

首先,创建文件yourapp/templatetags/value_from_settings.py,在其中定义您自己的新标签value_from_settings

from django.template import TemplateSyntaxError, Variable, Node, Variable, Library
from yourapp import settings

register = Library()
# I found some tricks in URLNode and url from defaulttags.py:
# https://code.djangoproject.com/browser/django/trunk/django/template/defaulttags.py
@register.tag
def value_from_settings(parser, token):
  bits = token.split_contents()
  if len(bits) < 2:
    raise TemplateSyntaxError("'%s' takes at least one " \
      "argument (settings constant to retrieve)" % bits[0])
  settingsvar = bits[1]
  settingsvar = settingsvar[1:-1] if settingsvar[0] == '"' else settingsvar
  asvar = None
  bits = bits[2:]
  if len(bits) >= 2 and bits[-2] == 'as':
    asvar = bits[-1]
    bits = bits[:-2]
  if len(bits):
    raise TemplateSyntaxError("'value_from_settings' didn't recognise " \
      "the arguments '%s'" % ", ".join(bits))
  return ValueFromSettings(settingsvar, asvar)

class ValueFromSettings(Node):
  def __init__(self, settingsvar, asvar):
    self.arg = Variable(settingsvar)
    self.asvar = asvar
  def render(self, context):
    ret_val = getattr(settings,str(self.arg))
    if self.asvar:
      context[self.asvar] = ret_val
      return ''
    else:
      return ret_val

您可以通过以下方式在模板中使用此标签:

% load value_from_settings %
[...]
% value_from_settings "FQDN" %

或通过

% load value_from_settings %
[...]
% value_from_settings "FQDN" as my_fqdn %

as ... 表示法的优势在于,它可以通过简单的my_fqdnblocktrans 块中轻松使用。

【讨论】:

【参考方案14】:

上面来自 bchhun 的示例很好,只是您需要从 settings.py 显式构建上下文字典。下面是一个 UNTESTED 示例,说明如何根据 settings.py 的所有大写属性自动构建上下文字典(re: "^[A-Z0-9_]+$")。

settings.py 结尾:

_context =  
local_context = locals()
for (k,v) in local_context.items():
    if re.search('^[A-Z0-9_]+$',k):
        _context[k] = str(v)

def settings_context(context):
    return _context

TEMPLATE_CONTEXT_PROCESSORS = (
...
'myproject.settings.settings_context',
...
)

【讨论】:

【参考方案15】:

另一种方法是创建一个自定义模板标签,它可以让您从设置中获取值。

@register.tag
def value_from_settings(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, var = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0]
    return ValueFromSettings(var)

class ValueFromSettings(template.Node):
    def __init__(self, var):
        self.arg = template.Variable(var)
    def render(self, context):        
        return settings.__getattr__(str(self.arg))

然后您可以使用:

% value_from_settings "FQDN" %

在任何页面上打印,无需跳过上下文处理器。

【讨论】:

我认为这是最优雅的解决方案,因为它可以作为 dropin 工作而无需更改代码。 您可以保持应用程序的其余部分保持不变:您添加一个标签并使用它,而不必添加上下文处理器(这意味着您必须在多个位置编辑您的应用程序)跨度> @Mark - 在 produi/src/produi/template_utils/templatetags/custom_template_filters.py 中 template_utils 引用自 settings.py INSTALLED_APPS - 另见docs.djangoproject.com/en/dev/howto/custom-template-tags 感谢 chris 的帮助,添加了一个带有 templatetags 子目录的 mutil 应用程序,其中包括 custom_template_filters。 homepage.html 中仍然出现错误“无效的块标签:'value_from_settings',预期的'endblock'或'endblock横幅'” 我认为这违背了“显式优于隐式”,使用上下文装饰器版本,您可以准确选择要公开的设置。【参考方案16】:

如果您使用 django 的内置通用视图或在 render_to_response 快捷方式中传递上下文实例关键字参数,Django 提供对模板的某些常用设置常量的访问,例如 settings.MEDIA_URL 和一些语言设置功能。以下是每种情况的示例:

from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic.simple import direct_to_template

def my_generic_view(request, template='my_template.html'):
    return direct_to_template(request, template)

def more_custom_view(request, template='my_template.html'):
    return render_to_response(template, , context_instance=RequestContext(request))

这些视图都有几个常用的设置,例如 settings.MEDIA_URL 可用于模板作为 MEDIA_URL 等。

如果您要访问设置中的其他常量,只需解压缩所需的常量并将它们添加到您在视图函数中使用的上下文字典中,如下所示:

from django.conf import settings
from django.shortcuts import render_to_response

def my_view_function(request, template='my_template.html'):
    context = 'favorite_color': settings.FAVORITE_COLOR
    return render_to_response(template, context)

现在您可以在模板上以 favorite_color 的身份访问settings.FAVORITE_COLOR

【讨论】:

值得注意的是,使用RequestContext添加的具体值取决于TEMPLATE_CONTEXT_PROCESSORS的值。因此,如果您想在任何地方传递其他值,只需编写您自己的上下文处理器并将其添加到 TEMPLATE_CONTEXT_PROCESSORS。 关于一致性的一点,在通用视图以及许多核心和贡献应用程序中,额外的上下文称为 extra_context,并且通常包含在视图的参数中。 "Django 提供对模板的某些常用设置常量的访问,例如 settings.MEDIA_URL"。这似乎在 Django 1.3 中不起作用,尽管我可能用错了。此功能是否有任何文档? @asofyan 是的,添加创建自定义模板上下文处理器并添加到 settings.py 中的 TEMPLATE_CONTEXT_PROCESSORS。 请查看django-settings-export 以避免在每个视图中都编写此代码。

以上是关于我可以从 Django 中的模板访问 settings.py 中的常量吗?的主要内容,如果未能解决你的问题,请参考以下文章

Django模板访问嵌套数据

如何从 Django 模板中的外键访问值

Django:从模板中访问会话变量?

从 django 模板访问 JSON 属性

在 Django 中,是不是可以从包含标记内的父模板上下文访问当前用户会话?

模板中的 Django 外键