Overriding settings
For testing purposes it's often useful to change a setting temporarily and revert to the original value after running the testing code. The following code doubles as a context manager and decorator. It's used like this:
from django.test import TestCase
from whatever import override_settings
class LoginTestCase(TestCase):
@override_settings(LOGIN_URL='/other/login/')
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
The decorator can also be applied to test case classes:
from django.test import TestCase
from whatever import override_settings
class LoginTestCase(TestCase):
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
LoginTestCase = override_settings(LOGIN_URL='/other/login/')(LoginTestCase)
On Python 2.6 and higher you can also use the well known decorator syntax to decorate the class:
from django.test import TestCase
from whatever import override_settings
@override_settings(LOGIN_URL='/other/login/')
class LoginTestCase(TestCase):
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
Alternatively it can be used as a context manager:
from django.test import TestCase
from whatever import override_settings
class LoginTestCase(TestCase):
def test_login(self):
with override_settings(LOGIN_URL='/other/login/'):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from __future__ import with_statement
from django.conf import settings, UserSettingsHolder
from django.utils.functional import wraps
class override_settings(object):
"""
Acts as either a decorator, or a context manager. If it's a decorator it
takes a function and returns a wrapped function. If it's a contextmanager
it's used with the ``with`` statement. In either event entering/exiting
are called before and after, respectively, the function/block is executed.
"""
def __init__(self, **kwargs):
self.options = kwargs
self.wrapped = settings._wrapped
def __enter__(self):
self.enable()
def __exit__(self, exc_type, exc_value, traceback):
self.disable()
def __call__(self, func):
@wraps(func)
def inner(*args, **kwargs):
with self:
return func(*args, **kwargs)
return inner
def enable(self):
override = UserSettingsHolder(settings._wrapped)
for key, new_value in self.options.items():
setattr(override, key, new_value)
settings._wrapped = override
def disable(self):
settings._wrapped = self.wrapped
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Hey. This is great, thanks.
Just to check I'm understanding correctly: The Django 1.2 docs specifically forbid changing settings anywhere other than in your settings files: https://docs.djangoproject.com/en/dev/topics/settings/#altering-settings-at-runtime
Is this because of the hazards of not undoing the changes that are wrought, thus leaving the application in an inconsistent state? (early-imported modules thing the settings say one thing, imports after your modification think the settings say another thing)
Does a snippet like this run the risk that any global state which depends on the modified settings, and is set within the scope of the context manager, will permanently be set to a value which is inconsistent with the rest of the application?
If so, presumably this can be avoided by not ever setting global state that depends on the values of settings - the settings should be referred to directly every time their value is needed.
Am I vaguely understanding correctly?
#
Please login first before commenting.