【问题标题】:In Django, is it possible to define a dynamically calculated setting based in another?在 Django 中,是否可以基于另一个定义动态计算的设置?
【发布时间】:2013-08-04 06:01:30
【问题描述】:

(也许这个问题更多是关于 Python,但 Django 是上下文,所以就这样吧)

假设您需要一个设置FOO,其值取决于设置BAR 的值(最简单的情况是使CELERY_RESULT_BACKEND 等于BROKER_URL)。

如果你只有一个设置文件,那很容易实现:

BAR = some_value
FOO = some_function(BAR)

然而,有很多设置文件是很流行的,每个环境(例如生产、开发、测试、阶段等)一个,正如《Django 的两个勺子:最佳实践》一书中的project layout 中所建议的那样对于 Django 1.5"。

在这种情况下,有一个 settings.base 模块,它的所有内容由 settings.devsettings.prod 等导入,它们添加自己的特定值或覆盖 settings.base 中定义的值。

当我想在其中一些模块中覆盖 BAR 时会出现问题,但我必须记住每次覆盖后都重新计算 FOO。这很容易出错,而且不会 DRY。

lambda 函数不起作用,因为该设置是可调用的,而不是结果值。内置函数/装饰器property 是理想的,但它只能在类中使用(新型)。我不知道其他类似的东西。

想法?

【问题讨论】:

  • 嗯。这有点不整洁,但也许将您的依赖设置拆分为不同的文件并在设置 BAR 后导入?

标签: python django settings


【解决方案1】:

有点骇人听闻:

class DynamicWrapper(object):
    _initialized = False

    def __init__(self, wrapped_name)
        self._wrapped = wrapped_name

    def __get__(self):
        if not self._initialized:
            self._initialized = True
            return self
        if not hasattr(self, '_computed_val'):
            from django.conf import settings
            val = getattr(settings, self._wrapped)
            self._computed_val = some_func(val)
        return self._computed_val

BAR = 'some_value'
FOO = DynamicWrapper('BAR')

思路如下:

  • Django 的设置对象在初始化设置对象时使用一次getattr(settings_module, setting)(请注意,当您执行from django.conf import settings 时,您实际上导入的是对象,而不是模块)。结果被设置为设置对象的属性。调用__get__,返回self,因此settings.FOODynamicWrapper 实例。
  • 当访问settings.FOO 时,这将在DynamicWrapper 对象上第二次调用__get__,此时您希望它返回实际值。该值是根据some_func 计算的,并返回该值而不是DynamicWrapper 对象。
  • 任何后续尝试获取settings.FOO 的值也将调用__get__ 方法,但这次计算的值被缓存为self._computed_val 并立即返回。

当有多个Settings 对象时,当FOO 以不同于通过django.conf.settings 的其他方式访问时,或者当BAR 未定义时,这不会处理(边缘)情况。

【讨论】:

  • 好主意,虽然这肯定是实现我想要的东西的骇人听闻的方式。我从来没有想过以这种方式解决问题。恭喜
【解决方案2】:

是的,设置文件也只是一个 python 脚本。

大多数人已经这样做来构建模板路径设置。

Django template Path

现在让我们想象一下:

ref = {
    'dev':  'FOO',
    'qa':   'BAR'
    'prod': 'BAZ'
}

ENV = 'PROD'  # Can also be DEV or QA

DYNAMIC_SETTING = ref.get(ENV.lower(), None)  # => 'BAZ'

【讨论】:

  • 我认为你误解了我的需要。您以DYNAMIC_SETTING 发布的内容并非如此:如果我在您的sn-p 之后更改ENV,然后再次访问DYNAMIC_SETTING,它的值不会改变。为什么?因为那行 (DYNAMIC_SETTING = ref ...) 已经被执行并且不会再次执行。
猜你喜欢
  • 2020-03-19
  • 1970-01-01
  • 2022-11-24
  • 2010-12-23
  • 2021-07-15
  • 2021-03-23
  • 2018-03-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多