I recently encountered a similar issue when upgrading simultaneously from Django 3.2 to Django 4.2 and then Django 5.2 at my job, the difference being that the postgresql version remained the same during the process (15.2.0).
It took longer to run the tests with each upgrade (1h35 with 3.2, 1h50 with 4.2 and more than 2h with 5.2). After some profiling, it turns out this was due to the iterations count of the PBKDF2 default password hasher, which rightfully keeps being increased with every Django release (I found out about that with this thread : Stop increasing default PBKDF2 iteration count).
To speed up tests, we decided it was OK to use a custom version of this hasher in tests only (we keep using the default, unmodified hasher in prod). To do so, all we had to do was to create a new hasher like this:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class TestPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
WARNING: This hasher is not suited for production!
Speed-up tests by lowering the iterations count as much as possible.
This protection does not seem to be required when running tests.
Note: The actual iterations count is increased with each Django release, e.g.:
- 260 000 iterations in Django 3.2
- 600 000 iterations in Django 4.2
- 1 000 000 iterations in Django 5.2
"""
iterations = 1
and then use it in our test settings:
# Speed-up tests by using an unsecure password hasher.
# WARNING: do not use this hasher in production!
PASSWORD_HASHERS = ["path.to.hasher_file.TestPBKDF2PasswordHasher"]
With this, the tests now run in 1h10! It is not optimal, and probably not that clean, but it currently fits our needs.
To be more exhaustive, I also ran the tests with iterations = 260_000 with Django 4.2, and it was already reducing our pipeline duration to less than 1h30. That was after this test that we decided it would probably be OK to reduce the iterations count to 1.
This might help others, this is why I’m sharing this.
I don’t know if the same time increase happens with other password hashers within Django; if so, I guess a similar solution could be used.