如何为自定义Django设置定义默认值

38

Django文档提到您可以将自己的设置添加到django.conf.settings中。因此,如果我的项目的settings.py定义了

APPLES = 1

在我的项目中,我可以通过在应用程序中使用settings.APPLES来访问它。

但是,如果我的settings.py没有定义该值,则访问settings.APPLES显然不起作用。是否有一种方法可以定义APPLES的默认值,以便在settings.py中没有明确设置时使用?

我希望最好在需要该设置的模块/包中定义默认值。

5个回答

35
我在我的应用中有一个单独的settings.py文件,在该文件中我有一个get()函数,它会在项目的settings.py文件中查找,如果没有找到则返回默认值。
from django.conf import settings

def get(key, default):
    return getattr(settings, key, default)


APPLES = get('APPLES', 1)

那么我需要访问苹果的地方是这样的:

from myapp import settings as myapp_settings

myapp_settings.APPLES

这允许在项目的 settings.py 中进行覆盖,getattr 首先会在那里检查并返回该属性的值(如果找到该属性),否则将使用应用程序设置文件中定义的默认值。


3
这个不是很脆弱吗?难道不依赖于在导入 app_settings.py 之前导入项目的 settings.py 文件吗? - Flimm
@Flimm 是的。如果您尝试直接导入 myapp_settings,它将在第一个 getattr 时初始化 Django 的 LazySettings,并给出配置错误(因为 此行 将返回部分评估的 myapp_settings 模块 而我们正在评估它,而不是进入无限循环导入)。我还没有找到一个干净的解决方法。 - scry

21

只是这样怎么样:

getattr(app_settings, 'SOME_SETTING', 'default value')

2
这比这里的其他建议简单得多,但如果您打算多次使用设置/变量,则会违反DRY概念,在这种情况下,其他解决方案可能有效。值得思考 :) - Daniel
1
它是如何违反DRY原则的? - Charlie
1
如果您计划在应用程序中多次使用SOME_SETTING,则必须将getattr(app_settings, 'SOME_SETTING', default_value)放置在每个使用它的位置。 - Daniel
无论您在哪里需要默认值。 - Charlie
app_settings 是从哪里来的?这应该是 django.conf.settings 吗? - run_the_race
显示剩余2条评论

8

这里有两种解决方案。你可以在应用程序中设置 settings.py 文件并填充默认值。

为单个应用程序配置默认值

在你的代码中使用 from MYAPP import settings 代替 from django.conf import settings

编辑 YOURAPP/__init__.py

from django.conf import settings as user_settings
from . import settings as default_settings

class AppSettings:
    def __getattr__(self, name):
        # If the setting you want is filled by the user, let's use it.
        if hasattr(user_settings, name):
            return getattr(user_settings, name)

        # If the setting you want has a default value, let's use it.
        if hasattr(default_settings, name):
            return getattr(default_settings, name)

        raise AttributeError("'Settings' object has no attribute '%s'" % name)

settings = AppSettings()

为整个项目配置默认值

在你的代码中使用from MYPROJECT import settings,而不是from django.conf import settings

编辑MYPROJECT/MYPROJECT/__init__.py

import os, sys, importlib
from . import settings as user_settings

def get_local_apps():
    """Returns the locally installed apps names"""
    apps = []
    for app in user_settings.INSTALLED_APPS:
        path = os.path.join(user_settings.BASE_DIR, app)
        if os.path.exists(path) and app != __name__:
            apps.append(sys.modules[app])
    return apps

class AppSettings:
    SETTINGS_MODULE = 'settings'

    def __getattr__(self, setting_name):

        # If the setting you want is filled by the user, let's use it.
        if hasattr(user_settings, setting_name):
            return getattr(user_settings, setting_name)

        # Let's check every local app loaded by django.
        for app in get_local_apps():
            module_source = os.path.join(app.__path__[0], "%s.py" % self.SETTINGS_MODULE)
            module_binary = os.path.join(app.__path__[0], "%s.pyc" % self.SETTINGS_MODULE)
            if os.path.exists(module_source) or os.path.exists(module_binary):
                module = importlib.import_module("%s.%s" % (app.__name__, self.SETTINGS_MODULE))

                # Let's take the first default value for this setting we can find in any app
                if hasattr(module, setting_name):
                    return getattr(module, setting_name)

        raise AttributeError("'Settings' object has no attribute '%s'" % setting_name)

settings = AppSettings()

这种解决方案看起来更容易安装,但不能保证返回良好的默认值。如果多个应用在它们的settings.py中声明同一变量,您无法确定哪个应用程序将返回您请求的默认值。


2

从Mike的回答开始,我将默认设置处理包装成了一个易于使用接口的类。

辅助模块:

from django.conf import settings

class SettingsView(object):
   class Defaults(object):
      pass

   def __init__(self):
      self.defaults = SettingsView.Defaults()

   def __getattr__(self, name):
      return getattr(settings, name, getattr(self.defaults, name))

使用方法:

from localconf import SettingsView

settings = SettingsView()
settings.defaults.APPLES = 1

print settings.APPLES

这将打印django.conf.settings中的值,如果在那里没有设置,则使用默认值。这个settings对象也可以用来访问所有标准设置值。


我认为应用程序特定设置应该明确命名并与主要设置不同,以便更容易阅读。 - Mike Ramirez

0

我最近遇到了同样的问题,并创建了一个Django应用程序,专门用于处理这种情况。它允许您为某些设置定义默认值。然后,它首先检查全局设置文件中是否设置了该设置。如果没有,则会返回默认值。

我还扩展了它,使其能够对默认值进行一些类型检查或预处理(例如,可以在加载时将点分路径转换为类本身)

该应用程序可以在此找到:https://pypi.python.org/pypi?name=django-pluggableappsettings&version=0.2.0&:action=display


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