我正在寻找一种方法,让应用程序默认设置和设置易于使用,难以出错,并且开销很小。
目前我的组织如下:
myapp/defaults.py
# application defaults
import sys
if sys.platform == 'win32':
MYAPP_HOME_ROOT = os.path.dirname(os.environ['USERPROFILE'])
else:
MYAPP_HOME_ROOT = '/home'
在我的项目中我有:
mysite/settings.py
from myapp.defaults import * # import all default from myapp
MYAPP_HOME_ROOT = '/export/home' # overriding myapp.defaults
通过此设置,我可以以常规django方式(from django.conf import settings
和settings.XXX
)导入和使用设置。
更新-3(为什么我们需要这个)
DEBUG
为True时,您经常想要做一些不同的事情,但任何其他全局设置都可能有用:例如MYAPP_IDENTICON_DIR = os.path.join(settings.MEDIA_ROOT, 'identicons')
(https://en.wikipedia.org/wiki/Identicon)MYAPP_IDENTICON_DIR = 's3://myappbucket.example.com/identicons'
文件中为其站点定义settings.py
的用户应获取此值,而不是应用程序&# 39;默认。import .. settings; settings.FOO
)的解决方案都优于需要新语法的解决方案(因为新语法会分歧,我们会从app获得新的独特方式来使用设置到app)。(原帖后面提出了两个关键问题,上述假设未说明。)
问题#1:在为应用运行单元测试时,没有网站,因此settings
不会有任何myapp.defaults
。
问题#2:如果myapp.defaults
需要使用设置中的任何内容(例如settings.DEBUG
),也会出现一个大问题,因为您无法导入设置来自defaults.py
(因为这将是循环导入)。
为解决问题#1,我创建了一个间接层:
myapp/conf.py
from . import defaults
from django.conf import settings
class Conf(object):
def __getattr__(self, attr):
try:
return getattr(settings, attr)
except AttributeError:
return getattr(defaults, attr)
conf = Conf() # !<-- create Conf instance
和用法:
myapp/views.py
from .conf import conf as settings
...
print settings.MYAPP_HOME_ROOT # will print '/export/home' when used from mysite
这允许conf.py
文件使用&#34;空&#34;设置文件也是如此,myapp代码可以继续使用熟悉的settings.XXX
。
它没有解决问题#2,基于例如定义应用程序设置。 settings.DEBUG
。我目前的解决方案是添加到Conf
类:
from . import defaults
from django.conf import settings
class Conf(object):
def __getattr__(self, attr):
try:
return getattr(settings, attr)
except AttributeError:
return getattr(defaults, attr)
if settings.DEBUG:
MYAPP_HOME_ROOT = '/Users'
else:
MYAPP_HOME_ROOT = '/data/media'
conf = Conf() # !<-- create Conf instance
但这并不令人满意,因为mysite不能再覆盖设置,而myapp的默认设置/设置现在分布在两个文件中......
有更简单的方法吗?
更新-4:&#34;只需使用django测试跑步者..&#34;
您正在测试的应用程序依赖于Django框架 - 您无法解决在测试应用程序之前需要首先引导框架的事实。引导过程的一部分是创建一个默认的settings.py,并进一步使用django提供的测试运行器来确保您的应用程序正在可能运行的环境中进行测试。
虽然这听起来应该是真的,但它实际上并没有多大意义,例如:没有默认的settings.py
(至少不适用于可重用的应用程序)。在谈论集成测试时,使用site / settings / apps / database(s)/ cache(s)/ resource-limits /等测试应用程序是有意义的。它将在生产中遇到。但是,对于单元测试,我们只想测试我们正在查看的代码单元 - 尽可能少的外部依赖(和设置)。 Django测试运行器做,并且应该模拟框架的主要部分,因此它不能说是在任何&#34;真实&#34;环境。
虽然Django测试运行器非常棒,但它有一长串问题无法处理。对我们来说,两个巨大的问题是(i)顺序运行测试是如此之慢以至于测试套件变得不被使用(并行运行时为5分钟,顺序运行时为1小时),(ii)有时您只需要对< em> big 数据库(我们将昨晚的备份恢复到测试可以运行的测试数据库 - 对于灯具来说太大了。)
制作鼻子,py.test,斜纹,Selenium和任何模糊测试工具的人确实知道测试很好,因为这是他们唯一关注的焦点。如果不能利用他们的集体经验将是一种耻辱。
我不是第一个遇到这个问题的人,并且看起来没有简单或常见的解决方案。以下是两个有不同解决方案的项目:
更新,python-oidc-provider方法:
python-oidc-provider包(https://github.com/juanifioren/django-oidc-provider)有另一种创造性的方法来解决app-settings / defaults问题。它使用属性来定义myapp/settings.py
文件中的默认值:
from django.conf import settings
class DefaultSettings(object):
@property
def MYAPP_HOME_ROOT(self):
return ...
default_settings = DefaultSettings()
def get(name):
value = None
try:
value = getattr(default_settings, name)
value = getattr(settings, name)
except AttributeError:
if value is None:
raise Exception("Missing setting: " + name)
使用myapp中的设置变为:
from myapp import settings
print settings.get('MYAPP_HOME_ROOT')
好:解决问题#2(在定义默认值时使用设置),解决问题#1(使用测试中的默认设置)。
坏:用于访问设置的不同语法(settings.get('FOO')
与普通settings.FOO
),myapp无法为在myapp之外使用的设置提供默认值(设置为来自from django.conf import settings
的get不包含myapp的任何默认值。外部代码可以from myapp import settings
进行常规设置和myapp默认设置,但如果有多个应用想要执行此操作,则会出现故障...
Update2,django-appconf包: (注意:与Django的AppConfig无关。)
使用django-appconfig
,应用程序设置在myapp/conf.py
中创建(需要尽早加载,因此您应该从models.py导入conf.py文件 - 因为它是早期加载的):
from django.conf import settings
from appconf import AppConf
class MyAppConf(AppConf):
HOME_ROOT = '...'
用法:
from myapp.conf import settings
print settings.MYAPP_HOME_ROOT
AppConf将自动添加MYAPP_
前缀,并自动检测项目设置中是否已重新定义/覆盖MYAPP_HOME_ROOT
。
pro:简单易用,解决问题#1(从测试中访问应用程序设置)和问题#2(在定义默认值时使用设置)。只要早期加载conf.py文件,外部代码就应该能够使用myapp中定义的默认值。
con:非常神奇。 conf.py中的设置名称与其用法不同(因为appconf会自动添加MYAPP_
前缀)。 Extenal / opaque依赖。
答案 0 :(得分:3)
我刚刚根据所有要求创建了django-app-defaults。它基本上是对问题(class Conf(object):
)中突出显示的第二种方法的概括。
用法:
# my_app/defaults.py
# `django.conf.settings` or any other module can be imported if needed
# required
DEFAULT_SETTINGS_MODULE = True
# define default settings below
MY_DEFAULT_SETTING = "yey"
然后在项目的任何地方:
from app_defaults import settings
print(settings.MY_DEFAULT_SETTING)
# yey
# All `django.conf.settings` are also available
print(settings.DEBUG)
# True
要为单个应用加载默认设置而不是所有应用,只需执行以下操作:
# Note: when apps or modules are explicitly passed,
# the `DEFAULT_SETTINGS_MODULE` var is not required
from app_defaults import Settings
settings = Settings(apps=["my_app"])
# or
from my_app import defaults
settings = Settings(modules=[defaults])
答案 1 :(得分:3)
我编写了一个django软件包,用于管理名为django-pluggableappsettings的应用程序设置。
它是一个软件包,允许您为设置定义合理的默认值,但还添加了类型检查或可调用设置等高级功能。它利用元类来轻松定义应用程序设置。当然,这会为您的项目添加外部依赖。
编辑1:
示例用法如下:
使用pip安装包:
pip install django-pluggableappsettings
在您的任何项目文件中创建AppSettings类。例如。在&#39; app_settings.py&#39;。
app_settings.py
from django_pluggableappsettings import AppSettings, Setting, IntSetting
class MyAppSettings(AppSettings):
MY_SETTING = Setting('DEFAULT_VALUE')
# Checks for "MY_SETTING" in django.conf.settings and
# returns 'DEFAULT_VALUE' if it is not present
ANOTHER_SETTING = IntSetting(42, aliases=['OTHER_SETTING_NAME'])
# Checks for "ANOTHER_SETTING" or "OTHER_SETTING_NAME" in
# django.conf.settings and returns 42 if it is not present.
# also validates that the given value is of type int
将MyAppSettings
导入任何文件并访问其属性
from mypackage.app_settings import MyAppSettings
MyAppSettings.MY_SETTING
MyAppSettings.ANOTHER_SETTING
请注意,设置仅在首次访问时初始化,因此,如果您从未访问过该设置,则永远不会检查其在django.conf.settings
中的状态。
答案 2 :(得分:2)
问题#1:当为应用程序运行单元测试时,没有站点 但是,所以设置不会有任何myapp.defaults。
通过使用django附带的测试框架(请参阅the documentation)解决此问题,因为它正确地为您引导测试环境。
请记住,django测试始终使用DEBUG=False
问题#2:如果myapp.defaults需要使用,还有一个大问题 设置中的任何内容(例如settings.DEBUG),因为您无法导入 来自defaults.py的设置(因为这将是循环导入)。
这不是一个真正的问题;从myapp.defaults
myapp.settings
导入后,settings
中的所有内容都将在范围内。所以你不要导入DEBUG
,它已经可以在全局范围内使用了。