Django可插拔应用程序的默认设置约定?

时间:2011-12-08 09:08:10

标签: python django configuration

如果在settings.py中未定义默认设置,那么在应用中处理默认设置的方式是什么?

我目前在应用中放置了一个default_settings文件,我考虑了一些选项。我倾向于第一种选择,但在使用globals()

时可能会有一些我不知道的陷阱

我经常看到应用程序在使用该设置的文件顶部执行FOO = getattr(settings, 'FOO', False)但我认为如果值/名称很长,则此方法存在可读性/重复性问题。


1:将设置放在函数中并迭代locals / set globals

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

setup_defaults()

优点:

  • 只需编写一次var name以从django设置中提取相同名称的默认值。

缺点:

  • 不习惯使用globals()并且不知道有任何影响

2:每次通话都写getattr(settings, 'MY_SETTING', default_settings.MY_SETTING)

优点: - 很清楚。

缺点: - 重复


3:始终将设置定义为FOO = getattr(settings, 'FOO', '...setting here...')

优点: - 始终覆盖默认值

缺点:

  • 重复(必须定义var两次 - 一次是字符串形式,一次是var)
  • 设置不具有可读性,因为它现在是第三个参数

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()

缺点:

  • 无法从foo.bar.my_settings导入FOO(实际上,这是一个可怕的交易破坏者!)

我很想听到反馈。

5 个答案:

答案 0 :(得分:40)

我认为在您的应用包中创建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一样,但如果您想为您的应用程序引入另外一个依赖项,那么您的决定就是这样!

答案 1 :(得分:4)

这个怎么样?

在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给出this answer,我觉得使用globals()就可以了。

答案 2 :(得分:4)

似乎我看到的每个解决方案都倾向于创建应用程序设置,代理,包装等内部副本。当设置为modified in run time like they do in tests时,这会让人感到困惑并产生问题。

对我来说,所有设置都属于django.conf.settings,只有那里。您不应该从其他地方读取它们,也不应将其复制以供以后使用(因为它们可能会更改)。您应该设置一次,不要在以后对默认值进行打扰。

我理解在内部使用应用设置时放弃应用前缀的冲动,但这也是恕我直言的一个坏主意。遇到麻烦时,查找SOME_APP_FOO不会产生结果,因为它在内部使用FOO。混淆吧?为什么,几封信?还记得that explicit is better吗?

恕我直言,最好的方法是在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')

或者更好的是在一个单独的模块中以简洁的方式定义它们,并将它们导入为{或3}} {(3}}:

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中的设置方式 - 如果您愿意,可以实施一些名称转换
  6. PS。有一个Settings class,但它没有解释原因。所以我认为这一次,在初始化期间可能是一个合理的例外;)

    PS2。请勿将单独的模块命名为settings,因为从settings导入django.conf时,这可能会让您感到困惑。

答案 3 :(得分:1)

回应Phil Gyford的comment,暴露了测试中未被覆盖的设置问题(因为已经在模块中导入),我所做的是在{中定义__init__.py类{1}}使用:

  • __init__方法,将每个设置初始化为None
  • 从{get>加载每个设置的load方法
  • 每个设置的静态getter

然后在代码中:

from . import AppSettings

def in_some_function():
    some_setting = AppSettings.get_some_setting()

或者,如果您想一次性加载它们(但是测试中的覆盖设置不会对受影响的模块起作用):

from . import AppSettings

app_settings = AppSettings()
app_settings.load()

def in_some_function():
   print(app_settings.some_setting)

然后,您可以在测试中使用override_settings装饰器,并且仍然有一些干燥且清晰的方式来使用应用程序设置,但每次要获取设置时执行的指令都会更多(仅用于测试) ...)。

答案 4 :(得分:-1)

3号是最好的,因为它是最简单的。而且非常一致。

1号:很容易忽视它。如果我打开你的代码,我不会滚动到底部,我会想念它,我会认为在我自己的模块中不能覆盖这些设置。

数字2:不仅重复,因为它太长而难以阅读,而且默认值将被多次定义并分散在您的代码中。

4号:不一致的重复呼叫。

数字5:不一致,我们希望在不在类中的模块中定义设置。好吧,至少我确实希望找到被定义为模块,因为我已经看到许多使用方法3的应用程序,我使用它自己,所以我可能有偏见。