Search code examples
djangoenvironment-variablesdjango-settingspython-packaging

How to create a django package without setting DJANGO_SETTINGS_MODULE as environment variable?


I am creating a package that itself uses Django and I will be using it within other Django applications. The main issue I am facing is that I need to use to settings for various reasons such as logging and other extensive requirements. Since, this package does not have any views/urls, we are writing tests and using pytest to run them. The tests will not run without the settings configured. So initially I put the following snippet in the __init__ file in the root app.

import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_package.settings")
django.setup()

Now, the test ran properly and the package as standalone app was working. But the moment I installed it in the main project, it overrides the enviroment variable with it's own settings and you can imagine the kind of havoc it would ensue.

This is the first time I am packaging a django app. So I am not well-versed with best practices and the docs are a little convoluted. I read the structure and code of various packages that use settings in their package but I am still not able to understand how to ensure the package accesses the intended settings and the project's settings is not affected at the same time.

While going throught the docs, I came accross this alternative to setting DJANGO_SETTINGS_MODULE, like this:

from django.conf import settings
settings.configure(DEBUG=True)

As shown here: https://docs.djangoproject.com/en/2.2/topics/settings/#using-settings-without-setting-django-settings-module

But where exactly am I supposed to add this? To every file where the settings are imported or will it work in the __init__ (Tried this but something isn't right, shows Apps aren't loaded )

I tried this as well where I imported my settings as defaults and called configure using them as defaults and called django.setup() as well but didn't do the trick:

# my_package/__init__.py

from django.conf import settings
from my_package import settings as default
if not settings.configured:
    settings.configure(default_settings=default, DEBUG=True)

import django
django.setup()

Also, I need settings mainly because I have few parameters that can be overridden in the project that is using the package. When the package is installed, the overridden variables is what I should be able to access in the package during runtime.

If someone can guide on how to tackle this or have a better process of creating packages that need django settings, please do share.


Solution

  • So I ended up finding a way to work without setting the settings module as an environement variable. This enables me to use the specified settings by importing all the overridden settings as well as the default settings from:

    Create a apps file for configuring your package as an app.

    # my_package/apps.py
    
    from django.apps import AppConfig
    
    
    class MyPackageConfig(AppConfig):
        name = 'my_package'
        verbose_name = 'My package'
    

    And, in your package's root. The following snippet in your __init__.py will only set the overridden settings:

    # my_package/__init__.py
    
    from django.conf import settings
    
    
    import django
    
    from my_package import settings as overridden_settings
    from django.conf import settings
    
    default_app_config = 'my_package.apps.MyPackageConfig'
    
    if not settings.configured:
        # Get the list of attributes the module has
        attributes = dir(overridden_settings)
        conf = {}
    
        for attribute in attributes:
            # If the attribute is upper-cased i.e. a settings variable, then copy it into conf
            if attribute.isupper():
                conf[attribute] = getattr(overridden_settings, attribute)
    
        # Configure settings using the settings
        settings.configure(**conf)
        
        # This is needed since it is a standalone django package
        django.setup()
    

    Reference for what django.setup() will do: https://docs.djangoproject.com/en/2.2/topics/settings/#calling-django-setup-is-required-for-standalone-django-usage

    Points to keep in mind:

    1. Since it is in the __init__, this will make sure if you import something from the package, the settings are configured.
    2. As mentioned in the documentation above, you have to make sure that the settings is configured only once and similarly the setup method is called once or it will raise an Exception.

    Let me know if this helps or you are able to come up with a better solution to this.