Django 可插拔应用的默认设置规范?

36 投票
6 回答
9159 浏览
提问于 2025-04-17 07:57

如果在 settings.py 中没有定义某个默认设置,处理这个问题的“django风格”方法是什么呢?

我现在在应用里放了一个 default_settings 文件,并考虑了几种选择。我倾向于第一种选择,但使用 globals() 可能会有我不知道的坑。

我大多数时候看到的做法是在使用设置的文件顶部写 FOO = getattr(settings, 'FOO', False),但我觉得如果值或名称很长,这种方法在可读性和重复性上会有问题。


1: 把设置放在一个函数里,然后遍历局部变量 / 设置全局变量

def setup_defaults():
    FOO = 'bar'
    for key, value in locals().items():
        globals()[key] = getattr(settings, key, value)

setup_defaults()

优点:

  • 只需写一次变量名,就能从django设置中提取同名的默认值。

缺点:

  • 不习惯使用 globals(),不知道会有什么影响。

2: 每次调用都写 getattr(settings, 'MY_SETTING', default_settings.MY_SETTING)

优点: - 非常清晰。

缺点: - 重复。


3: 总是定义设置为 FOO = getattr(settings, 'FOO', '...这里是默认设置...')

优点: - 默认值总是会被覆盖。

缺点:

  • 重复(必须定义变量两次 - 一次是字符串形式,一次是变量形式)。
  • 设置的可读性降低,因为现在是第三个参数。

4: 创建一个工具函数 get_or_default(setting)

优点:

  • 简单。
  • 不需要重复设置的字符串表示。

缺点:

  • 必须调用这个函数。

5: 创建一个设置类

class Settings(object):
    FOO = 'bar'

    def __init__(self):
         # filter out the startswith('__') of 
         # self.__dict__.items() / compare to django.conf.settings?

my_settings = Settings()

缺点:

  • 不能使用 from foo.bar.my_settings import FOO(实际上,这真的是个致命的缺陷!)

我很想听听大家的反馈。

6 个回答

5

这样怎么样?

在我的应用程序的设置文件 myapp/settings.py 中:

from django.conf import settings

FOO = 'bar'
BAR = 'baz'

_g = globals()
for key, value in _g.items():
    _g[key] = getattr(settings, key, value)

在我的应用程序的另一个文件 myapp/other.py 中:

import myapp.settings

print myapp.settings.FOO

根据 ncoghlan 的这个回答,我觉得这样使用 globals() 是可以的。

17

我发现这里的每个解决方案似乎都倾向于创建应用设置、代理或其他东西的内部副本。这让人感到困惑,并且在运行时修改设置时(就像在测试中那样)会产生问题,修改设置

在我看来,所有的设置都应该放在 django.conf.settings 里,只有这里。你不应该从其他地方读取它们,也不应该为了后续使用而复制它们(因为它们可能会改变)。你应该一次性设置好它们,之后就不用再担心默认值了。

我理解在内部使用应用设置时想去掉应用前缀的冲动,但在我看来这也是个坏主意。当你在找 SOME_APP_FOO 时,可能找不到结果,因为它在内部只是用 FOO。这不是很让人困惑吗?为了几个字母而这样做?记得 明确更好 吗?

在我看来,最好的方法就是在 Django 自己的设置中直接设置这些默认值,为什么不利用已经存在的管道呢?没有模块导入钩子,也不需要劫持 models.py 来初始化一些额外的复杂元类管道。

为什么不使用 AppConfig.ready 来设置默认值呢?

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from django.conf import settings
        settings = settings._wrapped.__dict__
        settings.setdefault('FOO_BAR_SETTING', 'whatever')

或者更好的是,在一个单独的模块中以简单明了的方式定义它们,并像 Settings 类 那样导入它们:

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from . import app_settings as defaults
        from django.conf import settings
        for name in dir(defaults):
            if name.isupper() and not hasattr(settings, name):
                setattr(settings, name, getattr(defaults, name))

我不确定使用 __dict__ 是否是最佳解决方案,但你明白我的意思,你总是可以使用 hasattr/setattr 的组合来实现这个效果。

这样你的应用设置就可以:

  1. 对其他人可见——如果他们在某些少见的情况下需要依赖它们,当然前提是应用之间的配置是相互依赖的
  2. 像其他设置一样正常读取
  3. 在自己的模块中很好地声明
  4. 足够懒惰
  5. 控制它们在 django.conf.settings 中的设置方式——如果你想的话,可以实现一些名称的转换

附言:有一个关于在运行时不修改设置的警告,但没有解释原因。所以我认为在初始化期间这一次可能是个合理的例外;)

附言2:不要把单独的模块命名为 settings,因为在从 django.conf 导入 settings 时可能会让人困惑。

46

我觉得在你的应用包里创建一个 settings.py 文件是很常见的做法,在这里你可以像这样定义你的设置:

from django.conf import settings
FOO = getattr(settings, 'FOO', "default_value")

在你的应用中,你可以从应用的 settings 模块中导入这些设置:

from myapp.settings import *

def print_foo():
    print FOO

不过大家都觉得Django在这方面的架构设计还有待改进!如果你想要更高级的处理方式,可以考虑一些第三方的应用,比如 django-appconf,但是否要引入这个额外的依赖就看你自己了!

2020年的更新

settings.py 中,把 settings.* 放在属性前面。

from django.conf import settings
settings.FOO = getattr(settings, 'FOO', "default_value")

撰写回答