在Django中定义常量

时间:2010-10-28 11:27:07

标签: python django constants

我想在Django项目中有一些常量。例如,假设一个名为MIN_TIME_TEST的常量。

我希望能够在两个地方访问此常量:从我的Python代码中,以及在任何模板中。

这样做最好的方法是什么?

修改 为了澄清,我知道模板上下文处理器以及只是将东西放在settings.py或其他文件中而只是导入。

我的问题是,如何在不违反“不要重复自己”规则的情况下结合这两种方法?基于到目前为止的答案,这是我的方法:

我想创建一个名为global_constants.py的文件,它有一个常量列表(像MIN_TIME_TEST = 5这样的东西)。我可以import将此文件放入任何模块中以获取常量。

但是现在,我想创建返回所有这些常量的上下文处理器。我怎样才能自动执行此操作,而无需在字典中再次列出它们,例如John Mee的答案?

7 个答案:

答案 0 :(得分:17)

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)

答案 1 :(得分:6)

基于其他人的答案,这里有一个简单的方法来实现这个:

在您的设置文件中:

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

然后,建立John Mee's context processor

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

答案 2 :(得分:3)

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

答案 3 :(得分:2)

使用context processors让你的常量在所有模板中都可用(settings.py是一个定义它们的好地方,如弗拉基米尔所说)。

答案 4 :(得分:1)

上下文处理器更适合处理更多动态对象数据 - 它们被定义为documentation中的映射,并且在这里的许多帖子中它们被修改或传递给视图 - 它模板可能无法访问全局信息是没有意义的,例如,您忘记在视图中使用专门的上下文处理器。根据定义,数据是全球性的。将视图耦合到模板。

更好的方法是定义自定义模板标记。这样:

  • 模板不依赖于视图来传递全局信息
  • 它是DRY-er:定义全局设置的应用程序可以导出到许多项目,从而消除项目中的公共代码
  • 模板决定他们是否有权访问全局信息,而不是视图函数

在下面的示例中,我处理您的问题 - 加载此MIN_TIME_TEST变量 - 以及我经常遇到的问题,加载在我的环境发生变化时更改的网址。

我有4个环境--2个开发和2个生产:

  • Dev:django-web server,url:localhost:8000
  • Dev:apache web server:url:sandbox.com - >解析为127.0.0.1
  • Prod sandbox server,url:sandbox.domain.com
  • 产品服务器: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标志更灵活。

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或暴露这些环境的其他项目可以简单地安装此应用程序=)

答案 5 :(得分: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)

答案 6 :(得分: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。 YMMV。