在Django中定义常量

24
我想在Django项目中设置一些常量,例如一个名为MIN_TIME_TEST的常量。
我希望能够在两个地方访问这个常量:从我的Python代码中和任何模板中。
最好的做法是什么?
编辑: 澄清一下,我知道模板上下文处理器以及将东西放在settings.py或其他文件中并进行导入。
我的问题是,如何将这两种方法结合起来,而不违反“不要重复自己”的原则?根据目前的答案,这是我的方法:
我想创建一个名为global_constants.py的文件,其中包含常量列表(像MIN_TIME_TEST = 5这样的东西)。我可以将此文件导入到任何模块中以获取这些常量。
但现在,我想创建返回所有这些常量的上下文处理器。我该如何自动完成此操作,而无需再次在字典中列出它们,就像John Mee的答案中那样?
7个回答

19

在我看来,Luper和Vladimir都是正确的,但为了完成您的要求,您需要两者都使用。

  • 虽然常量不一定需要放在settings.py中,您可以将它们放在任何地方,并从那个地方导入到您的视图/模型/模块代码中。如果我不想让它们被全局考虑,有时我会将它们放在__init__.py中。

  • 像这样的上下文处理器将确保所选变量在模板范围内全局可用。

def settings(request):
    """
    Put selected settings variables into the default template context
    """
    from django.conf import settings
    return {
        'DOMAIN':     settings.DOMAIN,
        'GOOGLEMAPS_API_KEY': settings.GOOGLEMAPS_API_KEY,
    }

如果你是 Django 初学者,那么这可能有些过度了;也许你只是想知道如何将变量放入模板作用域中...?

from django.conf import settings

...
# do stuff with settings.MIN_TIME_TEST as you wish

render_to_response("the_template.html", { 
    "MIN_TIME_TEST": settings.MIN_TIME_TEST 
}, context_instance=RequestContext(request)

谢谢,这确实是我想要的方向。但请看一下我的澄清/其他问题,即如何在不必两次列出所有常量的情况下做到你所说的事情? - Edan Maor
将它们全部包装到一个字典中,然后将该字典放入上下文中。您可以通过点符号访问它们。例如:{{yourDict.yourConstant}}。 - John Mee

6

在其他人的回答基础上,这里提供一种简单的实现方式:

在您的设置文件中:

GLOBAL_SETTINGS = {
    'MIN_TIME_TEST': 'blah',
    'RANDOM_GLOBAL_VAR': 'blah',
}

接着,基于John Mee的上下文处理器

def settings(request):
    """
    Put selected settings variables into the default template context
    """
    from django.conf import settings
    return settings.GLOBAL_SETTINGS

这将解决DRY问题。

或者,如果您只计划偶尔使用全局设置并希望从视图中调用它们:

def view_func(request):
    from django.conf import settings
    # function code here
    ctx = {} #context variables here
    ctx.update(settings.GLOBAL_SETTINGS)
    # whatever output you want here

3
考虑将其放入应用程序的settings.py中。当然,为了在模板中使用它,您需要将其作为任何其他常规变量一样使其可用于模板。

2
上下文处理器更适合处理更动态的对象数据——在文档中它们被定义为一个映射,在许多帖子中,它们被修改或传递到视图中——这是因为模板可能会失去对全局信息的访问,例如,您忘记在视图中使用专门的上下文处理器。数据是全局的,这意味着视图与模板耦合。
更好的方法是定义一个自定义模板标签。这样做的好处有:
  • 模板不依赖于视图传递全局信息
  • 它更加DRY(不重复),定义全局设置的应用程序可以导出到许多项目中,消除跨项目的公共代码
  • 模板决定它们是否有访问全局信息的权限,而不是视图函数
在以下示例中,我解决了您的问题——加载MIN_TIME_TEST变量——以及我经常遇到的问题,即在环境更改时加载URL。
我有4个环境——2个开发和2个生产环境:
  • Dev: django-web服务器,url:localhost:8000
  • Dev:apache web服务器:url:sandbox.com->解析为127.0.0.1
  • Prod sandbox服务器,url:sandbox.domain.com
  • Prod服务器:url:domain.com
我在所有项目中都这样做,并将所有URL保存在global_settings.py文件中,因此可以从代码中访问。我定义了一个自定义模板标签{% site_url%},它可以(可选)加载到任何模板中
我创建了一个名为global_settings的应用程序,并确保它包含在我的settings.INSTALLED_APPS元组中。
Django将模板文本编译为具有render()方法的节点,以告知如何显示数据——我创建了一个对象,通过根据传入的名称返回global_settings.py中的值来呈现数据。
它看起来像这样:
from django import template
import global_settings

class GlobalSettingNode(template.Node):
    def __init__(self, settingname):
        self.settingname = settingname;
    def render(self, context):
        if hasattr(global_settings, self.settingname):
            return getattr(global_settings, self.settingname)
        else:
            raise template.TemplateSyntaxError('%s tag does not exist' % self.settingname)

现在,在global_settings.py中,我注册了一些标签:例如我的site_url和你的min_test_time。这样,当从模板调用{% min_time_test %}时,它将调用get_min_time_test,该函数解析为加载值=5。在我的例子中,{% site_url %} 将进行基于名称的查找,以便我可以同时保留所有4个URL并选择我正在使用的环境。这对我来说比仅使用Django内置的settings.Debug=True/False标志更加灵活。请注意保留HTML标记。
from django import template
from templatenodes import GlobalSettingNode
register = template.Library()


MIN_TIME_TEST = 5

DEV_DJANGO_SITE_URL = 'http://localhost:8000/'
DEV_APACHE_SITE_URL = 'http://sandbox.com/'
PROD_SANDBOX_URL = 'http://sandbox.domain.com/'
PROD_URL = 'http://domain.com/'

CURRENT_ENVIRONMENT = 'DEV_DJANGO_SITE_URL'



def get_site_url(parser, token):
    return GlobalSettingNode(CURRENT_ENVIRONMENT)

def get_min_time_test(parser, token):
    return GlobalSettingNode('MIN_TIME_TEST')

register.tag('site_url', get_site_url)
register.tag('min_time_test', get_min_time_test)

请注意,为了使其正常工作,django期望global_settings.py位于Django应用程序下的一个名为templatetags的python包中。我的Django应用程序在这里称为global_settings,因此我的目录结构如下:
/project-name/global_settings/templatetags/global_settings.py 等等。
最后,模板选择是否加载全局设置是有益的性能优化。将以下行添加到您的模板中,以公开在global_settings.py中注册的所有标签:
{% load global_settings %}

现在,需要MIN_TIME_TEST或这些环境的其他项目只需安装此应用程序即可 =)

2

使用上下文处理器,使得你的常量在所有模板中都可用(像Vladimir所说的那样,在settings.py中定义它们是一个不错的选择)。


在将它们定义在settings.py文件中后,将它们添加到上下文处理器的好方法是什么?我不想重复所有常量的名称。 - Edan Maor
所有信息都可以在Luper Rouch链接的页面上找到。您需要创建一个Python函数,该函数将返回变量字典(这是您返回常量的地方),并将您的函数列在TEMPLATE_CONTEXT_PROCESSORS设置中。 - devmiles.com

0
在上下文处理器中,您可以使用类似以下的内容:
import settings

context = {}
for item in dir(settings):
    #use some way to exclude __doc__, __name__, etc..
    if item[0:2] != '__':
        context[item] = getattr(settings, item)

0

基于John Mee的最后一部分进行改编,对Jordan Reiter讨论的同样的想法进行了一些详细阐述。

假设你的设置中有类似于Jordan建议的内容 - 换句话说,类似于以下内容:

GLOBAL_SETTINGS = {
   'SOME_CONST': 'thingy',
   'SOME_OTHER_CONST': 'other_thingy',
}

假设您已经有一个字典,其中包含您想要传递给模板的某些变量,可能作为视图的参数传递。让我们称之为my_dict。假设您希望my_dict中的值覆盖settings.GLOBAL_SETTINGS字典中的值。
您可以在视图中执行以下操作:
def my_view(request, *args, **kwargs)
    from django.conf import settings
    my_dict = some_kind_of_arg_parsing(*args,**kwargs)
    tmp = settings.GLOBAL_SETTINGS.copy()
    tmp.update(my_dict)
    my_dict = tmp
    render_to_response('the_template.html', my_dict, context_instance=RequestContext(request))

这样可以让你在全局范围内确定设置,使其可用于模板,并且不需要手动输入每个设置。

如果你没有任何额外的变量要传递给模板,也没有任何需要覆盖的需求,你可以直接这样做:

render_to_response('the_template.html', settings.GLOBAL_SETTINGS, context_instance=RequestContext(request))

我在这里讨论的主要区别与Jordan所讨论的区别在于,对于他的情况,settings.GLOBAL_SETTINGS会覆盖任何与您上下文字典相同的内容,而对于我的情况,我的上下文字典会覆盖settings.GLOBAL_SETTINGS。具体情况可能因人而异。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接