gpt4 book ai didi

python - 模型使用时覆盖 Django 中的设置

转载 作者:太空宇宙 更新时间:2023-11-04 00:06:27 25 4
gpt4 key购买 nike

我们正在为 Speedy Net and Speedy Match 使用 Django (当前 Django 2.1)。我们的一些设置被模型使用。例如:

class USER_SETTINGS(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

MAX_NUMBER_OF_FRIENDS_ALLOWED = 800

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.USER_SETTINGS

(然后在类中使用settings的属性,比如settings.MIN_SLUG_LENGTH)。

问题是,当我尝试在测试中覆盖此类设置时(您可以在 Can I define classes in Django settings, and how can I override such settings in tests? 上查看我的问题和答案),User.settings 保持不变并且不会被设置覆盖我试图覆盖。这是一个问题,因为在模型中我将 settings.MIN_SLUG_LENGTH 传递给验证器,其他模型也将其他值传递给验证器。是否可以以在生产和测试中都使用正确设置的方式定义模型和设置,包括我想覆盖它们的时间?

我知道这句话来自 https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings :

Warning

The settings file contains some settings that are only consulted during initialization of Django internals. If you change them with override_settings, the setting is changed if you access it via the django.conf.settings module, however, Django’s internals access it differently. Effectively, using override_settings() or modify_settings() with these settings is probably not going to do what you expect it to do.

We do not recommend altering the DATABASES setting. Altering the CACHES setting is possible, but a bit tricky if you are using internals that make using of caching, like django.contrib.sessions. For example, you will have to reinitialize the session backend in a test that uses cached sessions and overrides CACHES.

Finally, avoid aliasing your settings as module-level constants as override_settings() won’t work on such values since they are only evaluated the first time the module is imported.

据我所知,在这种情况下哪些是相关的,但我如何以可以覆盖它们的方式定义设置?

函数_1___set_up在 speedy/core/base/test/models.py 中是使测试工作的解决方法,但这是一种 hack,我认为从长远来看这不是一个好的解决方案。

最佳答案

问题,如你所引:

avoid aliasing your settings as module-level constants as override_settings() won’t work on such values since they are only evaluated the first time the module is imported.

有 3 种方法,其中方式 1> 方式 3> 方式 2

方式 1:不要使用类属性作为别名,而是使用 classproperty

推荐;可以说是正确的方法。

  • 专业版:最具表现力,更易于调试。
  • 缺点:模型中有更多代码。
from django.utils.decorators import classproperty

class User(PermissionsMixin, Entity, AbstractBaseUser):
# settings = django_settings.USER_SETTINGS
@classproperty
def settings(cls):
return django_settings.USER_SETTINGS

警告:依赖于 settings 类属性的类属性将不起作用。

虽然方法 2 允许以下代码仍然有效,但这些代码是在类定义(导入)时评估的,不能根据 override_settings() 合理地更改,除非它们也是 classproperty

AGE_VALID_VALUES_IN_MODEL = range(settings.MIN_AGE_ALLOWED_IN_MODEL, settings.MAX_AGE_ALLOWED_IN_MODEL)
AGE_VALID_VALUES_IN_FORMS = range(settings.MIN_AGE_ALLOWED_IN_FORMS, settings.MAX_AGE_ALLOWED_IN_FORMS)

方式二:给设置类打补丁,让实例读取django_settings

不推荐; 在生产中也会影响 USER_SETTINGS 的运行时评估,而不仅仅是在测试中 (@hynekcer)

  • 优点:模型中没有代码更改。
  • 缺点:表达能力最差,更难调试。

  1. 定义一个函数overridable_settings:
def overridable_settings(settings_class):
old__getattribute__ = settings_class.__getattribute__
settings_name = settings_class.__name__

def patched__getattribute__(_self, item):
from django.conf import settings as django_settings
settings = getattr(django_settings, settings_name)
return old__getattribute__(settings, item)

settings_class.__getattribute__ = patched__getattribute__
return settings_class()
  1. django_settings.USER_SETTINGS 现在是设置类的一个实例。代替 get_django_settings_class_with_override_settings,定义 override_settings:
import copy

def override_settings(settings, **overrides):
copied_settings = copy.deepcopy(settings)
for setting, value in overrides.items():
setattr(copied_settings, setting, value)
assert copied_settings != settings
return copied_settings

用法:

@overridable_settings
class USER_SETTINGS(object):
from speedy.core.base.test import utils

# @override_settings(USER_SETTINGS=get_django_settings_class_with_override_settings(django_settings_class=django_settings.USER_SETTINGS, MIN_SLUG_LENGTH=tests_settings.OVERRIDE_USER_SETTINGS.MIN_SLUG_LENGTH))
@override_settings(USER_SETTINGS=utils.override_settings(django_settings.USER_SETTINGS, MIN_SLUG_LENGTH=tests_settings.OVERRIDE_USER_SETTINGS.MIN_SLUG_LENGTH))
def test_slug_min_length_fail_username_min_length_ok(self):

方式三:创建信号接收者setting_changed更新别名

  • 优点:对模型的代码更改最少。
  • 缺点:对于警告中的依赖属性,表现力不如方式 1

来自 https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings :

When overriding settings, make sure to handle the cases in which your app’s code uses a cache or similar feature that retains state even if the setting is changed. Django provides the django.test.signals.setting_changed signal that lets you register callbacks to clean up and otherwise reset state when settings are changed.

Django itself uses this signal to reset various data.

from django.core.signals import setting_changed
from django.dispatch.dispatcher import receiver

def register_django_setting_alias(setting_alias, django_setting):
def decorator(cls):
@receiver(setting_changed, weak=False)
def update_setting_alias(setting, value, **_):
if setting == django_setting:
setattr(cls, setting_alias, value)
return cls
return decorator

用法:

@register_django_setting_alias('settings', 'USER_SETTINGS')
class User(PermissionsMixin, Entity, AbstractBaseUser):
settings = django_settings.USER_SETTINGS

关于python - 模型使用时覆盖 Django 中的设置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53953444/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com