Могу ли я определить классы в настройках Django и как я могу переопределить такие настройки в тестах?
Мы используем Django для Speedy Net и Speedy Match (в настоящее время Django 1.11.17, мы не можем перейти на более новую версию Django из-за одного из наших требований, django-modeltranslation). Я хочу определить некоторые из наших настроек как классы. Например:
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
(а затем использовать атрибуты settings
, такие как settings.MIN_USERNAME_LENGTH
, в классе).
Но это исключение
AttributeError: 'Settings' object has no attribute 'UserSettings'
(но это не выдает исключение, если я использую там константу, которая не является классом).
Это первая проблема. Тем временем я определил вместо этого:
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
будет определен в классе UserSettings
как мне переопределить это?
2 ответа
Спасибо @Blender за подсказку:
Объект настроек Django явно пропускает любые объекты в вашем модуле настроек с не прописными именами. Если вы переименуете свой класс в USER_SETTINGS, он будет работать.
Я не знал, что все настройки должны быть прописными. Поэтому я переименовал class UserSettings
в class USER_SETTINGS
(хотя PyCharm это не нравится), но я проверил и также можно добавить этот код в конец файла:
USER_SETTINGS = UserSettings
Без переименования класса.
Что касается моего второго вопроса - как мне переопределить такие настройки в тестах? Я добавил файл с именем 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 django_settings_class_with_override_settings
потому что если я изменю класс django_settings_class
непосредственно это также влияет на другие тесты, которые не использовали @override_settings
,
Django не ожидает, что вы сильно отклонитесь от его низкоуровневых дизайнерских решений, и, как правило, трудно обойти вещи, которые Django явно не позволяет настраивать.
Объект настроек Django явно пропускает любые объекты в вашем модуле настроек с не прописными именами. Если вы переименуете свой класс в
USER_SETTINGS
, это будет работать. Если вы действительно хотите сохранить оригинальное имя вашего объекта, ужасным решением было бы обмануть Джанго:class UserSettings: ... class AlwaysUppercaseStr(str): def isupper(self): return True globals()[AlwaysUppercaseStr('UserSettings')] = globals().pop('UserSettings')
Я понятия не имею, является ли это переносимым между реализациями Python, но он работает с CPython
dir()
,override_settings
не поддерживает то, что вы пытаетесь сделать, поэтому вам, вероятно, придется переписать этот класс, чтобыsettings
объект для настройки.