Django @override_settings 不允许字典?

14 投票
4 回答
6544 浏览
提问于 2025-04-18 09:44

我刚接触Python装饰器,可能有些简单的地方没弄明白,这里是我的情况:

这个对我来说是有效的:

def test_something(self):
    settings.SETTING_DICT['key'] = True #no error
    ...

但是这个会报错:“SyntaxError: keyword can't be an expression”(语法错误:关键字不能是一个表达式):

@override_settings(SETTING_DICT['key'] = True) #error
def test_something(self):
   ...

为了说明清楚,正常使用覆盖设置是可以的:

@override_settings(SETTING_VAR = True) #no error
def test_something(self):
   ...

有没有办法用设置字典来使用装饰器,还是说我做错了什么?

提前谢谢大家!

4 个回答

0

对我来说最简单的解决方案(适用于Python 3.6):

@override_settings(SETTINGS_DICT={**SETTINGS_DICT, 'yournewkey': yournewvalue})
2

我也不想完全覆盖整个字典,所以我从settings对象里复制了那个字典,然后只修改了我感兴趣的属性:

import copy
from django.conf import settings
                                                                                                                                                     
settings_dict = copy.deepcopy(settings.SETTINGS_DICT)
settings_dict['key1']['key2'] = 'new value'

@override_settings(SETTINGS_DICT=settings_dict)
def test_something(self):
    pass

这样做对我来说足够用了,但如果你想让这个方法更通用一些,可以写一个简单的函数,给它几个参数,这样就能动态地做到类似的事情。

注意:我试过用settings.SETTINGS_DICT.copy()来代替copy.deepcopy(settings.SETTINGS_DICT),但那样似乎会把设置全局覆盖,影响到所有的测试。

8

从Python 3.3开始,如果你想用装饰器来覆盖字典(或者其他映射对象)中的特定值,同时又想让代码看起来更整洁,可以使用 collections.ChainMap。你可以在这里找到相关的文档:https://docs.python.org/3/library/collections.html#collections.ChainMap

from collections import ChainMap

@override_settings(SETTING_DICT=ChainMap({'key': 'value'}, settings.SETTING_DICT))
def test_something(self):
   ...

这样做不会覆盖整个字典,SETTINGS_DICT中的其他值仍然可以使用。

如果你在用Python 2.7,可以使用一个叫做backport的工具,它里面包含了ChainMap的实现。你可以在这里找到它:https://pypi.org/project/chainmap/

18

你应该重写整个字典:

@override_settings(SETTING_DICT={'key': True})
def test_something(self):
   ...

或者,你可以把 override_settings 当作上下文管理器来使用:

def test_something(self):
     value = settings.SETTING_DICT
     value['key'] = True
     with override_settings(SETTING_DICT=value):
         ...

撰写回答