Why Django's override_settings Sometimes Fails (and How reload + patch Saved Me)
Sometimes @override_settings
just doesn’t cut it.
I ran into a nasty issue while testing a Django module that relies on global state initialized during import. The usual test approach didn’t work. Here’s what happened and how I solved it.
The Setup
We had a module that builds a global dictionary from Django settings at import time. Let’s call it dragon.py
, which takes settings.PUT_EGGS
, which is False
by default:
from django.conf import settings
DRAGON = {}
...
if settings.PUT_EGGS:
DRAGON["eggs"] = "spam"
Another module uses DRAGON
for core logic, e.g. mario.py
:
from myproject.dragon import DRAGON
def find_eggs():
if "eggs" in DRAGON:
return "Found eggs!"
return "Eggs not found"
Now I wanted to write a test that tweaks DRAGON
and expects the logic to behave differently. Easy, right?
@override_settings(PUT_EGGS=True)
def test_find_eggs():
assert find_eggs() == "Found eggs!"
Wrong. The test failed.
The Problem
override_settings
works, but only for code that reads settings at runtime.
In my case, DRAGON
was already built at import time , before the override kicked in. So it used the old value of PUT_EGGS
, no matter what I did in the test.
This is the classic trap of global state baked during import. Welcome to pain town.
The Fix: reload + patch
Here's how I got out:
import importlib
from django.test import override_settings
from unittest.mock import patch
from myproject.mario import find_eggs
@override_settings(PUT_EGGS=True)
def test_find_eggs():
# Reload the dragon module so DRAGON is rebuilt
# with updated settings
from myproject import dragon
new_dragon = importlib.reload(dragon)
# Patch the logic module to use the reloaded DRAGON
with patch('myproject.mario.DRAGON', new_dragon.DRAGON):
result = find_eggs()
assert result == "Found eggs!"
Why This Works
importlib.reload(dragon)
forces a fresh import ofdragon
, rebuildingDRAGON
with the overridden settings;dragon.DRAGON
is updated in the scope of the test only, i.e.mario
module still has the stale version ofDRAGON;
patch(...)
solves this problem by swapping the oldDRAGON
inmario
with the freshly rebuilt one.
This is surgical. Ugly, but effective.
Lessons Learned
- Avoid putting non-trivial logic at module scope, especially if it depends on Django settings. Wrap it in a function or lazy loader.
- If you're stuck with global state,
reload()
andpatch()
give you a way out - just be careful about cascading dependencies.
If you’ve ever had a test mysteriously fail after overriding settings, this might be why.
Calculating the next run date of a Celery periodic task
Pytest Fish shell autocompletion
Typing repetitive commands or copying and pasting test names can eat up valuable time. To help, I've created pytest.fish
- a Fish shell plugin that simplifies your pytest workflow. It's lightweight, simple to set up, and makes testing more efficient.
How to Use
Autocomplete test paths
Type pytest
and hit TAB
to get suggestions for test paths and functions:

Support for -k
filter
Narrow down tests with -k
and get name suggestions:

The plugin dynamically scans your project, so suggestions stay up-to-date.
Installation
Install with Fisher:
fisher install ddoroshev/pytest.fish
Or manually copy the files from the repository into your Fish configuration.
How It Works
The plugin doesn't rely on pytest
directly (yet). Instead, it scans the current directory for test files and searches for test functions inside them, making the process relatively fast and efficient.
Other shells?
Since I primarily use Fish in my local development environment, I created a plugin specifically for this shell. However, if you use Bash or Zsh, feel free to create your own - or switch to Fish already. 😉
Spense.app v0.2
Hey everyone! I've finished working on the next version of Spense with a bunch of improvements and as per tradition I'm sharing the most interesting parts.
Accounts and Wallets Page
In the app interface, you can now manage your wallets and view the current balance:


Read more
Under the Hood of Spense.app: The Code.
While Spense v0.2 is under development, I want to talk about the internal organization of the application from a technical perspective. This article is mainly for web developers, and it's written in the corresponding language, so if you're reading and don't understand anything, that's okay, you can just skip it.
In a Nutshell
Backend on Django (Python), frontend on Django templates and Bootstrap, with a pinch of JavaScript and some htmx (not anymore).
Why So Boring?
Sounds not very hype, right. But remember that Spense in its current state is not a full-fledged product. It's more of a prototype, in which I often need to change things and test ideas. If the ideas work, I'll throw this code away and write another; if they don't, I'll just keep it for memory.
So, I chose Django not because I love it (actually, I hate it), but because I've gotten used to it over the last year, and it allows me to prototype the application easily and quickly.
Read more