df-config


Namedf-config JSON
Version 1.2.36 PyPI version JSON
download
home_pagehttps://github.com/d9pouces/df_config
SummarySmart default settings for Django websites
upload_time2024-02-25 20:52:01
maintainerMatthieu Gallet
docs_urlNone
authorMatthieu Gallet
requires_python>=3.9
licenseCECILL-B
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            df_config
=========

Django, *the web framework for perfectionists with deadlines*, is based on a single settings Python module defined in a environment variable.
However, these settings could be organized into three categories:

  * settings that are very common and that can be kept as-is for most projects (`USE_TZ = True` or `MEDIA_URL = '/media/'`),
  * settings that are specific to your project but common to all instances of your project (like `INSTALLED_APPS`),
  * settings that are installation-dependent (`DATABASE_PASSWORD`, …)


Moreover, there are dependencies between settings. For example, `ADMIN_EMAIL`, `ALLOWED_HOSTS` and `CSRF_COOKIE_DOMAIN` depend
 on the same domain name of your site,  and `SESSION_COOKIE_SECURE` and `CSRF_COOKIE_SECURE` can be set only when you use TLS.
df_config allows to use functions to dynamically define these settings using some other settings as parameters.


Finally, df_config dynamically merges several sources to define the final settings:

  * :mod:`df_config.config.defaults` that aims at providing good default values,
  * `yourproject.defaults` for your project-specific settings,
  * `/etc/yourproject/settings(.py|.ini)` for installation-dependent settings,
  * environment variables are also read if you prefer to use them.

df_config also defines settings that should be valid for most sites, based on common installed Django apps.

You can define a list of settings that are read from a traditionnal text configuration file (`.ini format <https://docs.python.org/3/library/configparser.html>`_).
Finally, df_config also searches for environment variables, or `local_settings.py` and `local_settings.ini` setting files in the working directory.


Requirements and installation
-----------------------------

df_config works with:

  * Python 3.6+,
  * Django 2.0+.
  
```bash
python -m pip install df_config
```
  
How to use it?
--------------

df_config assumes that your project has a main module `yourproject`.
Then you just have two steps to do:

- update your `manage.py` file: 

```python
#!/usr/bin/env python
from df_config.manage import manage, set_env

set_env(module_name="yourproject")
if __name__ == "__main__":
    manage()

```

- copy your current settings (as-is) into `yourproject/defaults.py`,


You can take a look to the resulting settings and from which source they are read:
```bash
python3 manage.py config python -v 2
```

If you want a single settings file, you can also create it:

```bash
python3 manage.py config python --filename settings.py
```


update your setup.py
--------------------

You should the entry point in your `setup.py` file:

```python
entry_points = {"console_scripts": ["yourproject-ctl = df_config.manage:manage"]}
``` 

Once installed, the command `yourproject-ctl` (in fact, you can change `ctl` by anything without hyphen) executes the standard `maanage` command. 


dynamize your settings!
-----------------------

First, you can provide sensible defaults settings in `yourproject.py` and overwrite the dev settings in `local_settings.py`.
Then real things can begin:
For example, imagine that you currently have the following settings:

```python
LOG_DIRECTORY = '/var/myproject/logs'
STATIC_ROOT = '/var/myproject/static'
MEDIA_ROOT = '/var/myproject/media'
FILE_UPLOAD_TEMP_DIR = '/var/myproject/tmp'
```

If you change the base directory `/var/myproject`, four variables needs to be changed (and you will forget to change at least one).
Now, you can write:

```python
LOCAL_PATH = '/var/myproject'
LOG_DIRECTORY = '{LOCAL_PATH}/logs'
STATIC_ROOT = '{LOCAL_PATH}/static'
MEDIA_ROOT = '{LOCAL_PATH}/media'
FILE_UPLOAD_TEMP_DIR = '{LOCAL_PATH}/tmp'
```

Now, you just have to redefine `LOCAL_PATH`; but you can even go slightly further:

```python
from df_config.config.dynamic_settings import Directory
LOCAL_PATH = Directory('/var/myproject')
LOG_DIRECTORY = Directory('{LOCAL_PATH}/logs')
STATIC_ROOT = Directory('{LOCAL_PATH}/static')
MEDIA_ROOT = Directory('{LOCAL_PATH}/media')
FILE_UPLOAD_TEMP_DIR = Directory('{LOCAL_PATH}/tmp')
```

If you run the `check` command, you will be warned for missing directories, and the `collectstatic` and `migrate` commands
will attempt to create them.
Of course, you still have `settings.MEDIA_ROOT == '/var/myproject/media'` in your code, when settings are loaded.


You can use more complex things, instead of:

```python
SERVER_BASE_URL = 'http://www.example.com'
SERVER_NAME = 'www.example.com'
USE_SSL = False
ALLOWED_HOSTS = ['www.example.com']
CSRF_COOKIE_DOMAIN = 'www.example.com'
```

You could just write:

```python
from urllib.parse import urlparse
from df_config.config.dynamic_settings import CallableSetting

SERVER_BASE_URL = 'http://www.example.com'
SERVER_NAME = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).hostname, 'SERVER_BASE_URL')
USE_SSL = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).scheme == 'https', 'SERVER_BASE_URL')
ALLOWED_HOSTS = CallableSetting(lambda x: [urlparse(x['SERVER_BASE_URL']).hostname], 'SERVER_BASE_URL')
CSRF_COOKIE_DOMAIN = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).hostname, 'SERVER_BASE_URL')
```

Configuration files and environment variables
---------------------------------------------

Your user probably prefer use .ini files instead of Python ones.
By default, df_config searches for a list `INI_MAPPING` into the module `yourproject.iniconf` or uses `df_config.iniconf.INI_MAPPING`.

```python
from df_config.config.fields import ConfigField
INI_MAPPING = [ConfigField("global.server_url", "SERVER_BASE_URL", help_str="Public URL of your website.", env_name="SERVER_BASE_URL")]
```

Some specialized classes are available in `df_config.config.fields`: `CharConfigField`, `IntegerConfigField`, `FloatConfigField`, `ListConfigField`, `BooleanConfigField`, `ChoiceConfigFile`.
You can also pickup some predefined list in `df_config.iniconf`.

You can also use environment variables instead of an .ini file (only for values in the INI_MAPPING list):
```bash
export SERVER_URL=http://www.example-2.com
python3 manage.py config python -v 2 | grep SERVER_BASE_URL
```

You can check the current config as a .ini file or as environment variables: 
```bash
export SERVER_URL=http://www.example-2.com
python3 manage.py config env
python3 manage.py config ini
```

dynamic settings
----------------

By default, any `str` is assumed to be a template string: for example, `LOG_DIRECTORY = '{LOCAL_PATH}/logs'` needs `LOCAL_PATH` to be defined.
So, if one of your setting include such values, it need to be encapsulated:

```python
from df_config.config.dynamic_settings import RawValue
LOG_DIRECTORY = RawValue('{LOCAL_PATH}/logs')
```

Other dynamic classes are:
```python
from df_config.config.dynamic_settings import Directory, AutocreateFileContent, SettingReference, CallableSetting, ExpandIterable
from df_config.guesses.misc import generate_secret_key, secure_hsts_seconds
DIRNAME = Directory("/tmp/directory")
# creates /tmp/directory with collectstatic/migrate commands, otherwise you have a warning 
SECRET_KEY = AutocreateFileContent("{LOCAL_PATH}/secret_key.txt", generate_secret_key, mode=0o600, use_collectstatic=False,use_migrate=True)
# if the file does not exist, SECRET_KEY = generate_secret_key()
# if the file does exist, SECRET_KEY is equal to its content
# if the migrate command is called and the file does not exist, it is created and the result of generate_secret_key() is written to it
TEMPLATE_DEBUG = SettingReference('DEBUG')
# TEMPLATE_DEBUG is always equal to DEBUG, even if DEBUG is defined in another file
SECURE_HSTS_SECONDS = CallableSetting(secure_hsts_seconds, "USE_SSL")
# the secure_hsts_seconds is called with a dict that contains the currently resolved settings
# at least USE_SSL is present in this dict (maybe some other values are also defined)
# the list of required settings can be directly set as an attribute of the callable:
# secure_hsts_seconds.required_settings = ["USE_SSL"]
# SECURE_HSTS_SECONDS = CallableSetting(secure_hsts_seconds)
INSTALLED_APPS = ["django.contrib.admin", ..., ExpandIterable("EXTRA_APPS"), "django.contrib.auth"]
# EXTRA_APPS must be a list, whose values are inserted in the final valaues

```

server command
--------------

You can choose the application server used in production:
```python
DF_SERVER = "gunicorn"  # "gunicorn" or "daphne"
```
A new Django command `server` is available and launches `gunicorn` or `daphne`. The application and the listen address/port are specified so you do no have to set them. 


Heroku
------

Environment variable names have been chosen to be compatible with the Heroku default environment: 

  * `SECRET_KEY`: should set it in your `app.json` file
  * `DATABASE_URL`: automatically set by the "heroku-postgresql" addon
  * `PORT`: set by default
  * `HEROKU_APP_NAME`: you should set it in your `app.json` file
  
Django app detection
--------------------

A few well-known Django applications are automatically detected and added to the list of `INSTALLED_APPS`:

  * `django-redis_sessions`
  * `django-pipeline`
  * `django-debug-toolbar`
  * `django-allauth`
  * `whitenoise`
  * `django-csp`
  
Defaults settings are also proposed so these applications should be working out of the box.



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/d9pouces/df_config",
    "name": "df-config",
    "maintainer": "Matthieu Gallet",
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "github@19pouces.net",
    "keywords": "",
    "author": "Matthieu Gallet",
    "author_email": "github@19pouces.net",
    "download_url": "https://files.pythonhosted.org/packages/6d/e5/fe8e9699787a003cc6b291d1c5699d003c1163355e4b3328842d97329d90/df_config-1.2.36.tar.gz",
    "platform": null,
    "description": "df_config\n=========\n\nDjango, *the web framework for perfectionists with deadlines*, is based on a single settings Python module defined in a environment variable.\nHowever, these settings could be organized into three categories:\n\n  * settings that are very common and that can be kept as-is for most projects (`USE_TZ = True` or `MEDIA_URL = '/media/'`),\n  * settings that are specific to your project but common to all instances of your project (like `INSTALLED_APPS`),\n  * settings that are installation-dependent (`DATABASE_PASSWORD`, \u2026)\n\n\nMoreover, there are dependencies between settings. For example, `ADMIN_EMAIL`, `ALLOWED_HOSTS` and `CSRF_COOKIE_DOMAIN` depend\n on the same domain name of your site,  and `SESSION_COOKIE_SECURE` and `CSRF_COOKIE_SECURE` can be set only when you use TLS.\ndf_config allows to use functions to dynamically define these settings using some other settings as parameters.\n\n\nFinally, df_config dynamically merges several sources to define the final settings:\n\n  * :mod:`df_config.config.defaults` that aims at providing good default values,\n  * `yourproject.defaults` for your project-specific settings,\n  * `/etc/yourproject/settings(.py|.ini)` for installation-dependent settings,\n  * environment variables are also read if you prefer to use them.\n\ndf_config also defines settings that should be valid for most sites, based on common installed Django apps.\n\nYou can define a list of settings that are read from a traditionnal text configuration file (`.ini format <https://docs.python.org/3/library/configparser.html>`_).\nFinally, df_config also searches for environment variables, or `local_settings.py` and `local_settings.ini` setting files in the working directory.\n\n\nRequirements and installation\n-----------------------------\n\ndf_config works with:\n\n  * Python 3.6+,\n  * Django 2.0+.\n  \n```bash\npython -m pip install df_config\n```\n  \nHow to use it?\n--------------\n\ndf_config assumes that your project has a main module `yourproject`.\nThen you just have two steps to do:\n\n- update your `manage.py` file: \n\n```python\n#!/usr/bin/env python\nfrom df_config.manage import manage, set_env\n\nset_env(module_name=\"yourproject\")\nif __name__ == \"__main__\":\n    manage()\n\n```\n\n- copy your current settings (as-is) into `yourproject/defaults.py`,\n\n\nYou can take a look to the resulting settings and from which source they are read:\n```bash\npython3 manage.py config python -v 2\n```\n\nIf you want a single settings file, you can also create it:\n\n```bash\npython3 manage.py config python --filename settings.py\n```\n\n\nupdate your setup.py\n--------------------\n\nYou should the entry point in your `setup.py` file:\n\n```python\nentry_points = {\"console_scripts\": [\"yourproject-ctl = df_config.manage:manage\"]}\n``` \n\nOnce installed, the command `yourproject-ctl` (in fact, you can change `ctl` by anything without hyphen) executes the standard `maanage` command. \n\n\ndynamize your settings!\n-----------------------\n\nFirst, you can provide sensible defaults settings in `yourproject.py` and overwrite the dev settings in `local_settings.py`.\nThen real things can begin:\nFor example, imagine that you currently have the following settings:\n\n```python\nLOG_DIRECTORY = '/var/myproject/logs'\nSTATIC_ROOT = '/var/myproject/static'\nMEDIA_ROOT = '/var/myproject/media'\nFILE_UPLOAD_TEMP_DIR = '/var/myproject/tmp'\n```\n\nIf you change the base directory `/var/myproject`, four variables needs to be changed (and you will forget to change at least one).\nNow, you can write:\n\n```python\nLOCAL_PATH = '/var/myproject'\nLOG_DIRECTORY = '{LOCAL_PATH}/logs'\nSTATIC_ROOT = '{LOCAL_PATH}/static'\nMEDIA_ROOT = '{LOCAL_PATH}/media'\nFILE_UPLOAD_TEMP_DIR = '{LOCAL_PATH}/tmp'\n```\n\nNow, you just have to redefine `LOCAL_PATH`; but you can even go slightly further:\n\n```python\nfrom df_config.config.dynamic_settings import Directory\nLOCAL_PATH = Directory('/var/myproject')\nLOG_DIRECTORY = Directory('{LOCAL_PATH}/logs')\nSTATIC_ROOT = Directory('{LOCAL_PATH}/static')\nMEDIA_ROOT = Directory('{LOCAL_PATH}/media')\nFILE_UPLOAD_TEMP_DIR = Directory('{LOCAL_PATH}/tmp')\n```\n\nIf you run the `check` command, you will be warned for missing directories, and the `collectstatic` and `migrate` commands\nwill attempt to create them.\nOf course, you still have `settings.MEDIA_ROOT == '/var/myproject/media'` in your code, when settings are loaded.\n\n\nYou can use more complex things, instead of:\n\n```python\nSERVER_BASE_URL = 'http://www.example.com'\nSERVER_NAME = 'www.example.com'\nUSE_SSL = False\nALLOWED_HOSTS = ['www.example.com']\nCSRF_COOKIE_DOMAIN = 'www.example.com'\n```\n\nYou could just write:\n\n```python\nfrom urllib.parse import urlparse\nfrom df_config.config.dynamic_settings import CallableSetting\n\nSERVER_BASE_URL = 'http://www.example.com'\nSERVER_NAME = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).hostname, 'SERVER_BASE_URL')\nUSE_SSL = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).scheme == 'https', 'SERVER_BASE_URL')\nALLOWED_HOSTS = CallableSetting(lambda x: [urlparse(x['SERVER_BASE_URL']).hostname], 'SERVER_BASE_URL')\nCSRF_COOKIE_DOMAIN = CallableSetting(lambda x: urlparse(x['SERVER_BASE_URL']).hostname, 'SERVER_BASE_URL')\n```\n\nConfiguration files and environment variables\n---------------------------------------------\n\nYour user probably prefer use .ini files instead of Python ones.\nBy default, df_config searches for a list `INI_MAPPING` into the module `yourproject.iniconf` or uses `df_config.iniconf.INI_MAPPING`.\n\n```python\nfrom df_config.config.fields import ConfigField\nINI_MAPPING = [ConfigField(\"global.server_url\", \"SERVER_BASE_URL\", help_str=\"Public URL of your website.\", env_name=\"SERVER_BASE_URL\")]\n```\n\nSome specialized classes are available in `df_config.config.fields`: `CharConfigField`, `IntegerConfigField`, `FloatConfigField`, `ListConfigField`, `BooleanConfigField`, `ChoiceConfigFile`.\nYou can also pickup some predefined list in `df_config.iniconf`.\n\nYou can also use environment variables instead of an .ini file (only for values in the INI_MAPPING list):\n```bash\nexport SERVER_URL=http://www.example-2.com\npython3 manage.py config python -v 2 | grep SERVER_BASE_URL\n```\n\nYou can check the current config as a .ini file or as environment variables: \n```bash\nexport SERVER_URL=http://www.example-2.com\npython3 manage.py config env\npython3 manage.py config ini\n```\n\ndynamic settings\n----------------\n\nBy default, any `str` is assumed to be a template string: for example, `LOG_DIRECTORY = '{LOCAL_PATH}/logs'` needs `LOCAL_PATH` to be defined.\nSo, if one of your setting include such values, it need to be encapsulated:\n\n```python\nfrom df_config.config.dynamic_settings import RawValue\nLOG_DIRECTORY = RawValue('{LOCAL_PATH}/logs')\n```\n\nOther dynamic classes are:\n```python\nfrom df_config.config.dynamic_settings import Directory, AutocreateFileContent, SettingReference, CallableSetting, ExpandIterable\nfrom df_config.guesses.misc import generate_secret_key, secure_hsts_seconds\nDIRNAME = Directory(\"/tmp/directory\")\n# creates /tmp/directory with collectstatic/migrate commands, otherwise you have a warning \nSECRET_KEY = AutocreateFileContent(\"{LOCAL_PATH}/secret_key.txt\", generate_secret_key, mode=0o600, use_collectstatic=False,use_migrate=True)\n# if the file does not exist, SECRET_KEY = generate_secret_key()\n# if the file does exist, SECRET_KEY is equal to its content\n# if the migrate command is called and the file does not exist, it is created and the result of generate_secret_key() is written to it\nTEMPLATE_DEBUG = SettingReference('DEBUG')\n# TEMPLATE_DEBUG is always equal to DEBUG, even if DEBUG is defined in another file\nSECURE_HSTS_SECONDS = CallableSetting(secure_hsts_seconds, \"USE_SSL\")\n# the secure_hsts_seconds is called with a dict that contains the currently resolved settings\n# at least USE_SSL is present in this dict (maybe some other values are also defined)\n# the list of required settings can be directly set as an attribute of the callable:\n# secure_hsts_seconds.required_settings = [\"USE_SSL\"]\n# SECURE_HSTS_SECONDS = CallableSetting(secure_hsts_seconds)\nINSTALLED_APPS = [\"django.contrib.admin\", ..., ExpandIterable(\"EXTRA_APPS\"), \"django.contrib.auth\"]\n# EXTRA_APPS must be a list, whose values are inserted in the final valaues\n\n```\n\nserver command\n--------------\n\nYou can choose the application server used in production:\n```python\nDF_SERVER = \"gunicorn\"  # \"gunicorn\" or \"daphne\"\n```\nA new Django command `server` is available and launches `gunicorn` or `daphne`. The application and the listen address/port are specified so you do no have to set them. \n\n\nHeroku\n------\n\nEnvironment variable names have been chosen to be compatible with the Heroku default environment: \n\n  * `SECRET_KEY`: should set it in your `app.json` file\n  * `DATABASE_URL`: automatically set by the \"heroku-postgresql\" addon\n  * `PORT`: set by default\n  * `HEROKU_APP_NAME`: you should set it in your `app.json` file\n  \nDjango app detection\n--------------------\n\nA few well-known Django applications are automatically detected and added to the list of `INSTALLED_APPS`:\n\n  * `django-redis_sessions`\n  * `django-pipeline`\n  * `django-debug-toolbar`\n  * `django-allauth`\n  * `whitenoise`\n  * `django-csp`\n  \nDefaults settings are also proposed so these applications should be working out of the box.\n\n\n",
    "bugtrack_url": null,
    "license": "CECILL-B",
    "summary": "Smart default settings for Django websites",
    "version": "1.2.36",
    "project_urls": {
        "Documentation": "https://github.com/d9pouces/df_config",
        "Homepage": "https://github.com/d9pouces/df_config",
        "Repository": "https://github.com/d9pouces/df_config"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "018a25faf2da299cab5790b58380366ea52024db58cde1889584ce415825867a",
                "md5": "9707ca99b3841a926412d369b0e089fa",
                "sha256": "3c5bf22449ef1edabd9c7c571b742dfd9f4d9444ee8705d615b2f3b23645b2b8"
            },
            "downloads": -1,
            "filename": "df_config-1.2.36-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9707ca99b3841a926412d369b0e089fa",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 158950,
            "upload_time": "2024-02-25T20:51:58",
            "upload_time_iso_8601": "2024-02-25T20:51:58.497484Z",
            "url": "https://files.pythonhosted.org/packages/01/8a/25faf2da299cab5790b58380366ea52024db58cde1889584ce415825867a/df_config-1.2.36-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6de5fe8e9699787a003cc6b291d1c5699d003c1163355e4b3328842d97329d90",
                "md5": "0c455394ccf1e1bcc4663d884983a540",
                "sha256": "93374dc9089da6353398f233d69d73059b9357bdafacf352590eedd030101117"
            },
            "downloads": -1,
            "filename": "df_config-1.2.36.tar.gz",
            "has_sig": false,
            "md5_digest": "0c455394ccf1e1bcc4663d884983a540",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 141367,
            "upload_time": "2024-02-25T20:52:01",
            "upload_time_iso_8601": "2024-02-25T20:52:01.585324Z",
            "url": "https://files.pythonhosted.org/packages/6d/e5/fe8e9699787a003cc6b291d1c5699d003c1163355e4b3328842d97329d90/df_config-1.2.36.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-25 20:52:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "d9pouces",
    "github_project": "df_config",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "df-config"
}
        
Elapsed time: 0.19872s