jazzband / django-configurations

A helper for organizing Django project settings by relying on well established programming patterns.
https://django-configurations.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.1k stars 143 forks source link

Make magical importing optional #360

Open spookylukey opened 1 year ago

spookylukey commented 1 year ago

It seems that django-configurations provides a number of functionalities:

  1. A method for organising settings via inheritance hierarchies
  2. A bunch of utilities for defining settings based on environment variables, URLs etc.
  3. It's own way of defining and importing settings, via the DJANGO_CONFIGURATION variable, which involves a bit of "magic".

The first two are great, but it seems like this last part is really unrelated, and causes a lot of complexity and issues. The docs say:

Yes, it looks like magic, but it’s also maintainable and non-intrusive

I think this is a bit optimistic. It requires you to change your manage.py, or wsgi.py/asgi.py, so it is quite invasive, it monkey patches BaseCommand, it breaks any script that just uses django.setup(), and also creates a bunch of other bugs, such as:

Is all of this actually necessary, or actually worth it compared to other options?

My idea is that you would not have any import loader (or it would be optional). Instead, you'd have a normal settings.py file, which would contain something like:

from configurations import setup_settings

setup_settings(from_environment_variable='DJANGO_CONFIGURATION')

All of the magic needed would be in that function. It would ideally not do any monkey patching of Django, but it might well do some messing around with sys._getframe(1).f_globals etc. to populate the module.

In other words, you'd have a couple of lines of boilerplate in each project, but only in the settings.py file. The only magic would be that the settings.py module explicitly populates itself in an unusual way, and after that there would be zero consequences for everything else.

Proof of concept - settings.py:

from configurations import setup_settings

setup_settings()

print(A_MAGICALLY_CREATED_SETTING)

configurations/__init__.py:


def setup_settings():
    f = sys._getframe(1)
    f.f_globals["A_MAGICALLY_CREATED_SETTING"] = "MAGICALLY INJECTED!"

You could also pass in globals() like setup_settings(globals()) for even less magic.