Techniques to declare settings in a third party Django library
Tags: django, settings, thirdparty
Published: 2021-07-23 17:59:07

Following up my article [How to set up and run migrations in third-party Django packages](https://dev.to/rydra/how-to-set-up-and-run-migrations-in-third-party-django-packages-44ln), another question I faced when developing third-party Django libraries was: how can/should I implement the settings for the application in a decoupled and reusable manner, while allowing users of my library configure the settings?

## The problem of using `django.conf.settings` directly in a third-party library

In Django, whenever you need to access a setting, unless you wrap them in a class, you have to import the `settings` module from `django.conf`.

For example:

```python
from django.conf import settings
a = 10
b = a * settings.MULTIPLICATION_FACTOR
```

Can I you use this import inside your third party library? Sure, you could. However this poses three problems:

1. You need to document that the developer using your library **MUST** specify the `MULTIPLICATION_FACTOR`. Otherwise, codes like the previous example will crash.
2. You cannot define a default value for the variables of your library.
3. And again, unless you document your library very well, the settings your library uses are scattered everywhere, making it difficult to identify what I can customize.

## A proposed better solution: the wrapper class

This is a solution I found when looking at the source code of the well known package [Django Corsheaders](https://github.com/adamchainz/django-cors-headers). It is very, very simple. Just define a wrapper over the `django.conf.settings`, and specify a property per each setting that your library allows to customize.

Example:

```python
from django.conf import settings

class Settings:
    @property
    def MULTIPLICATION_FACTOR(self):
        return getattr(settings, "MULTIPLICATION_FACTOR", 2)

settings = Settings()
```

Then we would modify the first code example like this:

```python
from your_django_module.conf import settings
a = 10
b = a * settings.MULTIPLICATION_FACTOR
```

From that point forward, every time I would need a setting in my third-party library I would use that alternative import. 

The `Settings` class and the `MULTIPLICATION_FACTOR` property are quite straightforward to explain: it checks if the user has defined an override for the settings called "MULTIPLICATION_FACTOR". If he/she did, take that value, otherwise, default to 2.

This simple trick decouples you furthermore from the Django infrastructure, which promotes cleaner and more reusable code.

## Alternative technique

I'm not going to go in-depth with it, but I've seen a different technique used to achieve the same result in the [SimpleJWT project](https://github.com/jazzband/djangorestframework-simplejwt) that can be found in [this file in the source code](https://github.com/jazzband/djangorestframework-simplejwt/blob/master/rest_framework_simplejwt/settings.py). Basically the technique relies on the `settings_changed` signal to establish default values for settings and update those settings in the context of the package. I'm not sure if there is a massive improvement on this approach over the previous one (maybe caching?), but if there is, feel free to comment.