我可以在 Django 设置中定义 类 吗?如何在测试中覆盖这些设置?
Can I define classes in Django settings, and how can I override such settings in tests?
我们正在为 Speedy Net and Speedy Match 使用 Django(目前是 Django 1.11.17,由于我们的要求之一,django-modeltranslation,我们无法升级到更新版本的 Django)。我想将我们的一些设置定义为 classes。例如:
class UserSettings(object):
MIN_USERNAME_LENGTH = 6
MAX_USERNAME_LENGTH = 40
MIN_SLUG_LENGTH = 6
MAX_SLUG_LENGTH = 200
# Users can register from age 0 to 180, but can't be kept on the site after age 250.
MIN_AGE_ALLOWED_IN_MODEL = 0 # In years.
MAX_AGE_ALLOWED_IN_MODEL = 250 # In years.
MIN_AGE_ALLOWED_IN_FORMS = 0 # In years.
MAX_AGE_ALLOWED_IN_FORMS = 180 # In years.
MIN_PASSWORD_LENGTH = 8
MAX_PASSWORD_LENGTH = 120
PASSWORD_VALIDATORS = [
{
'NAME': 'speedy.core.accounts.validators.PasswordMinLengthValidator',
},
{
'NAME': 'speedy.core.accounts.validators.PasswordMaxLengthValidator',
},
]
(在 https://github.com/speedy-net/speedy-net/blob/staging/speedy/net/settings/global_settings.py 中定义)。然后在模型中,我尝试使用:
from django.conf import settings as django_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = django_settings.UserSettings
(然后在class中使用settings
的属性,比如settings.MIN_USERNAME_LENGTH
)。
但是抛出异常
AttributeError: 'Settings' object has no attribute 'UserSettings'
(但如果我在那里使用一个不是 class 的常量,它不会抛出异常)。
这是第一个问题。与此同时,我改为定义:
from speedy.net.settings import global_settings as speedy_net_global_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = speedy_net_global_settings.UserSettings
第二个问题,我如何在测试中覆盖这些设置?例如,我使用以下代码:
from speedy.core.settings import tests as tests_settings
@override_settings(MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_MAX_NUMBER_OF_FRIENDS_ALLOWED)
在 https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/friends/tests/test_views.py。但是,如果 MAX_NUMBER_OF_FRIENDS_ALLOWED
将在 class UserSettings
中定义,我该如何覆盖它?
Django 不希望您偏离它的低级设计选择太多,并且通常很难解决 Django 未明确允许您自定义的事情。
Django 的设置对象 explicitly skips over 设置模块中具有非大写名称的任何对象。如果您将 class 重命名为 USER_SETTINGS
,它将起作用。如果您真的想要保留对象的原始名称,一个可怕的解决方案是欺骗 Django:
class UserSettings:
...
class AlwaysUppercaseStr(str):
def isupper(self):
return True
globals()[AlwaysUppercaseStr('UserSettings')] = globals().pop('UserSettings')
我不知道这是否可以跨 Python 实现移植,但它适用于 CPython 的 dir()
.
override_settings
不支持您尝试执行的操作,因此您可能需要重写 class 以允许全局 settings
对象可配置。
感谢@Blender 的提示:
Django's settings object explicitly skips over any objects in your
settings module with non-uppercase names. If you rename your class to
USER_SETTINGS, it will work.
我不知道所有的设置都必须是大写的。所以我把class UserSettings
重命名为class USER_SETTINGS
(虽然PyCharm不喜欢),但是我检查了一下,也可以在文件末尾添加这段代码:
USER_SETTINGS = UserSettings
不重命名class。
关于我的第二个问题 - 我如何在测试中覆盖这些设置?我添加了一个名为 utils.py
:
的文件
def get_django_settings_class_with_override_settings(django_settings_class, **override_settings):
class django_settings_class_with_override_settings(django_settings_class):
pass
for setting, value in override_settings.items():
setattr(django_settings_class_with_override_settings, setting, value)
return django_settings_class_with_override_settings
(在https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/base/test/utils.py上可以看到)
然后在测试中:
from django.conf import settings as django_settings
from django.test import override_settings
from speedy.core.settings import tests as tests_settings
from speedy.core.base.test.utils import get_django_settings_class_with_override_settings
@override_settings(USER_SETTINGS=get_django_settings_class_with_override_settings(django_settings_class=django_settings.USER_SETTINGS, MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED))
def test_user_can_send_friend_request_if_not_maximum(self):
self.assertEqual(first=django_settings.USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED, second=4)
我检查了,我必须定义另一个 class(在这种情况下,class django_settings_class_with_override_settings
因为如果我直接更改 class django_settings_class
它也会影响其他测试没有使用 @override_settings
.
我们正在为 Speedy Net and Speedy Match 使用 Django(目前是 Django 1.11.17,由于我们的要求之一,django-modeltranslation,我们无法升级到更新版本的 Django)。我想将我们的一些设置定义为 classes。例如:
class UserSettings(object):
MIN_USERNAME_LENGTH = 6
MAX_USERNAME_LENGTH = 40
MIN_SLUG_LENGTH = 6
MAX_SLUG_LENGTH = 200
# Users can register from age 0 to 180, but can't be kept on the site after age 250.
MIN_AGE_ALLOWED_IN_MODEL = 0 # In years.
MAX_AGE_ALLOWED_IN_MODEL = 250 # In years.
MIN_AGE_ALLOWED_IN_FORMS = 0 # In years.
MAX_AGE_ALLOWED_IN_FORMS = 180 # In years.
MIN_PASSWORD_LENGTH = 8
MAX_PASSWORD_LENGTH = 120
PASSWORD_VALIDATORS = [
{
'NAME': 'speedy.core.accounts.validators.PasswordMinLengthValidator',
},
{
'NAME': 'speedy.core.accounts.validators.PasswordMaxLengthValidator',
},
]
(在 https://github.com/speedy-net/speedy-net/blob/staging/speedy/net/settings/global_settings.py 中定义)。然后在模型中,我尝试使用:
from django.conf import settings as django_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = django_settings.UserSettings
(然后在class中使用settings
的属性,比如settings.MIN_USERNAME_LENGTH
)。
但是抛出异常
AttributeError: 'Settings' object has no attribute 'UserSettings'
(但如果我在那里使用一个不是 class 的常量,它不会抛出异常)。
这是第一个问题。与此同时,我改为定义:
from speedy.net.settings import global_settings as speedy_net_global_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = speedy_net_global_settings.UserSettings
第二个问题,我如何在测试中覆盖这些设置?例如,我使用以下代码:
from speedy.core.settings import tests as tests_settings
@override_settings(MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_MAX_NUMBER_OF_FRIENDS_ALLOWED)
在 https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/friends/tests/test_views.py。但是,如果 MAX_NUMBER_OF_FRIENDS_ALLOWED
将在 class UserSettings
中定义,我该如何覆盖它?
Django 不希望您偏离它的低级设计选择太多,并且通常很难解决 Django 未明确允许您自定义的事情。
Django 的设置对象 explicitly skips over 设置模块中具有非大写名称的任何对象。如果您将 class 重命名为
USER_SETTINGS
,它将起作用。如果您真的想要保留对象的原始名称,一个可怕的解决方案是欺骗 Django:class UserSettings: ... class AlwaysUppercaseStr(str): def isupper(self): return True globals()[AlwaysUppercaseStr('UserSettings')] = globals().pop('UserSettings')
我不知道这是否可以跨 Python 实现移植,但它适用于 CPython 的
dir()
.override_settings
不支持您尝试执行的操作,因此您可能需要重写 class 以允许全局settings
对象可配置。
感谢@Blender 的提示:
Django's settings object explicitly skips over any objects in your settings module with non-uppercase names. If you rename your class to USER_SETTINGS, it will work.
我不知道所有的设置都必须是大写的。所以我把class UserSettings
重命名为class USER_SETTINGS
(虽然PyCharm不喜欢),但是我检查了一下,也可以在文件末尾添加这段代码:
USER_SETTINGS = UserSettings
不重命名class。
关于我的第二个问题 - 我如何在测试中覆盖这些设置?我添加了一个名为 utils.py
:
def get_django_settings_class_with_override_settings(django_settings_class, **override_settings):
class django_settings_class_with_override_settings(django_settings_class):
pass
for setting, value in override_settings.items():
setattr(django_settings_class_with_override_settings, setting, value)
return django_settings_class_with_override_settings
(在https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/base/test/utils.py上可以看到)
然后在测试中:
from django.conf import settings as django_settings
from django.test import override_settings
from speedy.core.settings import tests as tests_settings
from speedy.core.base.test.utils import get_django_settings_class_with_override_settings
@override_settings(USER_SETTINGS=get_django_settings_class_with_override_settings(django_settings_class=django_settings.USER_SETTINGS, MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED))
def test_user_can_send_friend_request_if_not_maximum(self):
self.assertEqual(first=django_settings.USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED, second=4)
我检查了,我必须定义另一个 class(在这种情况下,class django_settings_class_with_override_settings
因为如果我直接更改 class django_settings_class
它也会影响其他测试没有使用 @override_settings
.