Stay informed of it
Project description
Django Dynamic Settings
Django Dynamic Settings allows you to create & use dynamic settings backed by a database.
Installation
Installation using pip:
pip install dj-dynamic-settings
dj_dynamic_settings app has to be added to INSTALLED_APPS and migrate command has to be run.
INSTALLED_APPS = (
# other apps here...
"dj_dynamic_settings",
)
dj_dynamic_settings.urls must be included to a desired url path.
urlpatterns = [
...,
url(r"^api/v1/", include("dj_dynamic_settings.urls")),
]
Setting class must be defined & registered. Please make sure that this class' module runs whenever the application runs.
from dj_dynamic_settings.registry import BaseSetting, registry
from dj_dynamic_settings.validators import TypeValidator
@registry.register
class FeatureActive(BaseSetting):
key = "FEATURE_ACTIVE"
validators = [TypeValidator(bool)]
default = False
description = "Flag for Feature X"
Create Setting instance using view.
import requests
requests.post(
url="https://your-app.com/api/v1/dynamic_settings/",
headers={
"Authorization": "Token <secret-login-token>",
},
json={
"key": "FEATURE_ACTIVE",
"value": True,
"is_active": True,
}
)
Access this setting as in django.conf.settings
from dj_dynamic_settings.conf import settings
settings.FEATURE_ACTIVE # True
Create / Update Triggers
To fire a callback method when a specific setting value updated or created, you can implement post_save_actions in BaseSetting inherited class
Following example shows how to implement post_save_actions method.
The callback method will be called with following kwargs:
key=instance.key
value=instance.value
created=created # is create operation
Note: post_save_actions returns an array, so you can add multiple callback methods. These callback methods will be called synchronously.
class PostUpdateTestConfiguration(BaseSetting):
key = "X_FEATURE_POST_UPDATE"
validators = [...]
@classmethod
def post_save_actions(cls):
return [
on_data_updated,
]
def on_data_updated(*args, **kwargs):
pass
Testing Tools
override_settings()
You can override a setting for a test method or test class.
from dj_dynamic_settings.utils import override_settings
from django.test import TestCase
@override_settings(SOME_SETTING="some_setting")
class FeatureTestCase(TestCase):
@override_settings(SOME_OTHER_SETTING="SOME_OTHER_SETTING")
def test_feature(self):
# Some stuff
pass
def test_feature_x(self):
with override_settings(SOME_OTHER_SETTING="SOME_OTHER_SETTING"):
# Some stuff
pass
Selective Field-Level Encryption
dj-dynamic-settings supports selective Fernet encryption of sensitive field values. Configuration is two-pronged:
- Code-defined defaults via Django setting:
DYNAMIC_SETTINGS_ENCRYPTED_FIELDS = ["password", "api_key", "token"]
- Runtime via the
/encrypted_keys/REST API or admin tooling. Admins POST/DELETE entries against theEncryptedKeyendpoint to add/remove encrypted fields without a code deploy.
Both sources are merged via a frozenset union — duplicates dedup automatically.
Setting value JSON content is scanned recursively:
- If the Setting's own
keyis in the active field-name set, the entire value is encrypted (scalar case). - Inside dicts (at any nesting depth), keys matching the active set have their values encrypted.
- Inside lists, each element is recursed.
Decryption is transparent — your code reads settings.X_FOO and gets plaintext, no changes required:
from dj_dynamic_settings.conf import settings
settings.X_PAYMENT_GATEWAY # returns plaintext, decrypted on the fly
Cache (Django cache framework) stores ciphertext only; decryption happens at return time. API GET responses replace encrypted fields with "***ENCRYPTED***" (configurable via DYNAMIC_SETTINGS_ENCRYPTION_MASK).
Encryption key:
- Default: derived from Django's
SECRET_KEYvia SHA256 → urlsafe base64. Zero setup required. - Optional override: set
DYNAMIC_SETTINGS_ENCRYPTION_KEYto a stable 32-byte urlsafe base64 Fernet key. Recommended ifSECRET_KEYis ever rotated.
When an EncryptedKey row is created or deleted through the API, all existing Settings are scanned and re-encrypted or decrypted symmetrically, atomically within the request. See Encrypting existing data below for the non-API path and the immutability of field_name.
Encrypting existing data
The DYNAMIC_SETTINGS_ENCRYPTED_FIELDS setting is the source of truth for which
fields are encrypted at rest. Reconcile the EncryptedKey table to match it with:
python manage.py sync_encrypted_fields # apply the reconcile
python manage.py sync_encrypted_fields --dry-run # print the plan, write nothing
The command mirrors the table to the list, in one transaction:
- A field newly added to the list → an
EncryptedKeyrow is created and all existing matchingSettingdata is encrypted. - A field removed from the list → existing data is decrypted at rest and the row is deleted (a WARNING is logged for each decrypted field).
Run it once per deploy, after migrate. On Akinon Cloud, add it to the
akinon.json release script:
"release": "python manage.py migrate --no-input && python manage.py sync_encrypted_fields"
The list is authoritative for the entire
EncryptedKeytable. Any row whosefield_nameis not inDYNAMIC_SETTINGS_ENCRYPTED_FIELDSis removed and its data decrypted on the next run — including rows created through thePOST /encrypted_keysAPI. A project that runs this command should manage encrypted fields exclusively through the settings list, not the API.
Removing a field decrypts data at rest. An accidental edit to the list will decrypt sensitive values on the next deploy. Use
--dry-runto preview, and review list changes carefully.
field_name is immutable on an existing EncryptedKey (a PATCH/PUT changing it
returns HTTP 400).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dj_dynamic_settings-0.3.0-py3-none-any.whl.
File metadata
- Download URL: dj_dynamic_settings-0.3.0-py3-none-any.whl
- Upload date:
- Size: 19.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.8.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f13cf96cea774f83705fecc0c578b33911b2ccf52cdc3bf0968491be0838c33
|
|
| MD5 |
cc41d5826b917f9215a7eb9f04ca72d3
|
|
| BLAKE2b-256 |
ababf2ee39a9d61bdf489061c85b31e911e9b79154a3cbb19b5f18b521f470d9
|