2014-11-25 20:58:02 +00:00
Constance - Dynamic Django settings
===================================
Features
--------
* Easily migrate your static settings to dynamic settings.
2020-02-12 19:23:11 +00:00
* Edit the dynamic settings in the Django admin interface.
2014-11-25 20:58:02 +00:00
2024-06-14 14:57:30 +00:00
.. image :: _static/screenshot2.png
2014-11-25 21:47:20 +00:00
2020-02-18 14:24:37 +00:00
Quick Installation
------------------
2014-11-25 20:58:02 +00:00
2021-09-09 11:08:35 +00:00
.. code-block ::
2016-01-01 11:35:46 +00:00
pip install "django-constance[redis]"
2014-11-25 20:58:02 +00:00
2020-02-18 14:24:37 +00:00
For complete installation instructions, including how to install the
database backend, see :ref: `Backends <backends>` .
2014-11-25 20:58:02 +00:00
Configuration
-------------
Modify your `` settings.py `` . Add `` 'constance' `` to your
:setting: `INSTALLED_APPS` , and move each key you want to turn dynamic into
the :setting: `CONSTANCE_CONFIG` section, like this:
.. code-block :: python
INSTALLED_APPS = (
2016-03-28 19:54:17 +00:00
'django.contrib.admin',
'django.contrib.staticfiles',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
2014-11-25 20:58:02 +00:00
...
'constance',
)
CONSTANCE_CONFIG = {
'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, '
'The Universe, and Everything'),
}
2019-12-23 19:17:42 +00:00
.. note :: Add constance *before* your project apps.
2020-02-12 19:23:11 +00:00
.. note :: If you use admin extensions like
2022-10-10 21:08:54 +00:00
`Grapelli <https://grappelliproject.com/> `_ , `` 'constance' `` should be added
2020-02-12 19:23:11 +00:00
in :setting: `INSTALLED_APPS` *before* those extensions.
2018-04-01 05:08:22 +00:00
2014-11-25 20:58:02 +00:00
Here, `` 42 `` is the default value for the key `` THE_ANSWER `` if it is
not found in the backend. The other member of the tuple is a help text the
admin will show.
2014-12-05 01:24:25 +00:00
See the :ref: `Backends <backends>` section how to setup the backend and
finish the configuration.
2014-11-25 20:58:02 +00:00
2016-03-04 15:32:44 +00:00
`` django-constance `` 's hashes generated in different instances of the same
2016-05-30 04:04:40 +00:00
application may differ, preventing data from being saved.
2016-03-04 15:32:44 +00:00
2020-02-12 19:23:11 +00:00
Use :setting: `CONSTANCE_IGNORE_ADMIN_VERSION_CHECK` in order to skip hash
verification.
2016-03-04 15:32:44 +00:00
.. code-block :: python
CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True
2016-09-14 17:37:19 +00:00
Signals
-------
2016-09-15 18:58:30 +00:00
Each time a value is changed it will trigger a `` config_updated `` signal.
2016-09-14 17:37:19 +00:00
.. code-block :: python
2016-09-14 17:39:00 +00:00
2016-09-14 19:53:52 +00:00
from constance.signals import config_updated
2016-09-14 17:37:19 +00:00
2016-09-14 19:53:52 +00:00
@receiver(config_updated)
2017-01-31 15:41:55 +00:00
def constance_updated(sender, key, old_value, new_value, **kwargs):
print(sender, key, old_value, new_value)
2016-09-14 17:37:19 +00:00
2018-08-29 11:48:07 +00:00
The sender is the `` config `` object, and the `` key `` and `` new_value ``
2020-02-12 19:23:11 +00:00
are the changed settings.
2016-09-14 17:37:19 +00:00
2015-06-08 14:13:40 +00:00
Custom fields
-------------
2016-09-15 18:58:30 +00:00
You can set the field type with the third value in the `` CONSTANCE_CONFIG `` tuple.
2016-03-27 03:08:45 +00:00
2025-08-02 19:15:29 +00:00
The value can be one of the supported types or a string matching a key in your :setting: `CONSTANCE_ADDITIONAL_FIELDS`
2016-03-27 03:08:45 +00:00
The supported types are:
2016-09-15 18:58:30 +00:00
* `` bool ``
* `` int ``
* `` float ``
* `` Decimal ``
* `` str ``
* `` datetime ``
2025-01-08 08:46:06 +00:00
* `` timedelta ``
2016-09-15 18:58:30 +00:00
* `` date ``
* `` time ``
2024-09-04 09:41:53 +00:00
* `` list ``
* `` dict ``
2016-03-27 03:08:45 +00:00
2025-08-02 19:29:36 +00:00
.. note ::
To be able to use `` list `` and `` dict `` you need to set a widget and form field for these types as it is ambiguous what types shall be stored in the collection object.
You can do so with :setting: `CONSTANCE_ADDITIONAL_FIELDS` as explained below.
2016-03-27 03:08:45 +00:00
For example, to force a value to be handled as a string:
2015-06-08 14:13:40 +00:00
.. code-block :: python
2016-03-12 08:38:40 +00:00
2015-06-08 14:13:40 +00:00
'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, '
'The Universe, and Everything', str),
2025-08-02 19:15:29 +00:00
Custom field types are supported using the dictionary :setting: `CONSTANCE_ADDITIONAL_FIELDS` .
2016-03-27 03:08:45 +00:00
This is a mapping between a field label and a sequence (list or tuple). The first item in the sequence is the string
2016-03-27 03:11:12 +00:00
path of a field class, and the (optional) second item is a dictionary used to configure the field.
2016-03-27 03:08:45 +00:00
2016-09-15 18:58:30 +00:00
The `` widget `` and `` widget_kwargs `` keys in the field config dictionary can be used to configure the widget used in admin,
the other values will be passed as kwargs to the field's `` __init__() ``
2016-03-27 03:08:45 +00:00
2025-01-07 14:34:32 +00:00
.. note :: Use later evaluated strings instead of direct classes for the field and widget classes:
2015-06-08 14:13:40 +00:00
.. code-block :: python
2016-03-12 08:38:40 +00:00
2015-06-08 14:13:40 +00:00
CONSTANCE_ADDITIONAL_FIELDS = {
2016-03-12 08:38:40 +00:00
'yes_no_null_select': ['django.forms.fields.ChoiceField', {
'widget': 'django.forms.Select',
2016-11-27 05:18:56 +00:00
'choices': ((None, "-----"), ("yes", "Yes"), ("no", "No"))
2016-03-12 08:38:40 +00:00
}],
2015-06-08 14:13:40 +00:00
}
2016-03-12 08:38:40 +00:00
CONSTANCE_CONFIG = {
'MY_SELECT_KEY': ('yes', 'select yes or no', 'yes_no_null_select'),
}
2015-06-08 14:13:40 +00:00
2023-07-29 16:35:38 +00:00
If you want to work with images or files you can use this configuration:
2017-11-06 16:40:53 +00:00
.. code-block :: python
CONSTANCE_ADDITIONAL_FIELDS = {
'image_field': ['django.forms.ImageField', {}]
}
CONSTANCE_CONFIG = {
'LOGO_IMAGE': ('default.png', 'Company logo', 'image_field'),
}
When used in a template you probably need to use:
.. code-block :: html
{% load static %}
{% get_media_prefix as MEDIA_URL %}
2022-04-24 06:23:50 +00:00
<img src="{{ MEDIA_URL }}{{ config.LOGO_IMAGE }}">
2017-11-06 16:40:53 +00:00
2023-07-29 16:35:38 +00:00
Images and files are uploaded to `` MEDIA_ROOT `` by default. You can specify a subdirectory of `` MEDIA_ROOT `` to use instead by adding the `` CONSTANCE_FILE_ROOT `` setting. E.g.:
2017-11-06 16:40:53 +00:00
2023-07-29 16:35:38 +00:00
.. code-block :: python
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
CONSTANCE_FILE_ROOT = 'constance'
This will result in files being placed in `` media/constance `` within your `` BASE_DIR `` . You can use deeper nesting in this setting (e.g. `` constance/images `` ) but other relative path components (e.g. `` ../ `` ) will be rejected.
2017-11-06 16:40:53 +00:00
2025-08-02 19:29:36 +00:00
In case you want to store a list of `` int `` values in the constance config, a working setup is
.. code-block :: python
CONSTANCE_ADDITIONAL_FIELDS = {
list: ["django.forms.fields.JSONField", {"widget": "django.forms.Textarea"}],
}
CONSTANCE_CONFIG = {
'KEY': ([0, 10, 20], 'A list of integers', list),
}
Make sure to use the `` JSONField `` for this purpose as user input in the admin page may be understood and saved as `` str `` otherwise.
2016-09-10 11:23:52 +00:00
Ordered Fields in Django Admin
2016-11-27 05:55:21 +00:00
------------------------------
2016-09-10 10:48:52 +00:00
2020-02-12 19:23:11 +00:00
To sort the fields, you can use an OrderedDict:
2016-09-10 10:48:52 +00:00
.. code-block :: python
from collections import OrderedDict
CONSTANCE_CONFIG = OrderedDict([
('SITE_NAME', ('My Title', 'Website title')),
('SITE_DESCRIPTION', ('', 'Website description')),
('THEME', ('light-blue', 'Website theme')),
2016-11-16 17:01:19 +00:00
])
2016-09-14 17:37:19 +00:00
2016-09-10 10:48:52 +00:00
2016-08-22 17:39:23 +00:00
Fieldsets
2016-09-10 10:48:52 +00:00
---------
2016-08-22 17:39:23 +00:00
2020-02-12 19:23:11 +00:00
You can define fieldsets to group settings together:
2016-08-22 17:39:23 +00:00
.. code-block :: python
CONSTANCE_CONFIG = {
'SITE_NAME': ('My Title', 'Website title'),
'SITE_DESCRIPTION': ('', 'Website description'),
'THEME': ('light-blue', 'Website theme'),
}
CONSTANCE_CONFIG_FIELDSETS = {
'General Options': ('SITE_NAME', 'SITE_DESCRIPTION'),
'Theme Options': ('THEME',),
}
2018-08-29 11:48:07 +00:00
2020-02-12 19:23:11 +00:00
.. note :: CONSTANCE_CONFIG_FIELDSETS must contain all fields from CONSTANCE_CONFIG.
2018-03-29 03:30:07 +00:00
2024-06-14 14:57:30 +00:00
.. image :: _static/screenshot3.png
2016-08-22 17:39:23 +00:00
2020-03-16 16:33:19 +00:00
Fieldsets collapsing
--------------------
2022-04-28 20:23:45 +00:00
To make some fieldsets collapsing you can use new format in CONSTANCE_CONFIG_FIELDSETS. Here's an example:
2020-03-16 16:33:19 +00:00
.. code-block :: python
CONSTANCE_CONFIG = {
'SITE_NAME': ('My Title', 'Website title'),
'SITE_DESCRIPTION': ('', 'Website description'),
'THEME': ('light-blue', 'Website theme'),
}
CONSTANCE_CONFIG_FIELDSETS = {
'General Options': {
'fields': ('SITE_NAME', 'SITE_DESCRIPTION'),
'collapse': True
},
'Theme Options': ('THEME',),
}
2022-07-13 15:01:25 +00:00
Field internationalization
--------------------------
Field description and fieldset headers can be integrated into Django's
internationalization using the `` gettext_lazy `` function. Note that the
`` CONSTANCE_CONFIG_FIELDSETS `` must be converted to a tuple instead of dict
as it is not possible to have lazy proxy objects as dictionary keys in the
settings file. Example:
.. code-block :: python
from django.utils.translation import gettext_lazy as _
CONSTANCE_CONFIG = {
'SITE_NAME': ('My Title', _('Website title')),
'SITE_DESCRIPTION': ('', _('Website description')),
'THEME': ('light-blue', _('Website theme')),
}
CONSTANCE_CONFIG_FIELDSETS = (
(
_('General Options'),
{
'fields': ('SITE_NAME', 'SITE_DESCRIPTION'),
'collapse': True,
},
),
(_('Theme Options'), ('THEME',)),
)
2014-11-25 20:58:02 +00:00
Usage
-----
Constance can be used from your Python code and from your Django templates.
Python
^^^^^^
Accessing the config variables is as easy as importing the config
object and accessing the variables with attribute lookups::
from constance import config
# ...
if config.THE_ANSWER == 42:
answer_the_question()
2026-03-04 22:37:37 +00:00
Asynchronous usage
^^^^^^^^^^^^^^^^^^
If you are using Django's asynchronous features (like async views), you can `` await `` the settings directly on the standard `` config `` object::
from constance import config
async def my_async_view(request):
# Accessing settings is awaitable
if await config.THE_ANSWER == 42:
return await answer_the_question_async()
async def update_settings():
# Updating settings asynchronously
await config.aset('THE_ANSWER', 43)
# Bulk retrieval is supported as well
values = await config.amget(['THE_ANSWER', 'SITE_NAME'])
Performance and Safety
~~~~~~~~~~~~~~~~~~~~~~
While synchronous access (e.g., `` config.THE_ANSWER `` ) still works inside async views for some backends, it is highly discouraged:
* **Blocking:** Synchronous access blocks the event loop, reducing the performance of your entire application.
* **Safety Guards:** For the Database backend, Django's safety guards will raise a `` SynchronousOnlyOperation `` error if you attempt to access a setting synchronously from an async thread.
* **Automatic Detection:** Constance will emit a `` RuntimeWarning `` if it detects synchronous access inside an asynchronous event loop, helping you identify and fix these performance bottlenecks.
For peak performance, especially with the Redis backend, always use the `` await `` syntax which leverages native asynchronous drivers.
2014-11-25 20:58:02 +00:00
Django templates
^^^^^^^^^^^^^^^^
2020-02-12 19:23:11 +00:00
To access the config object from your template you can
2014-11-25 20:58:02 +00:00
pass the object to the template context:
.. code-block :: python
from django.shortcuts import render
from constance import config
def myview(request):
return render(request, 'my_template.html', {'config': config})
2020-02-12 19:23:11 +00:00
You can also use the included context processor.
2014-11-25 20:58:02 +00:00
2020-02-12 19:23:11 +00:00
Insert `` 'constance.context_processors.config' `` at
2017-10-19 02:24:03 +00:00
the top of your `` TEMPLATES['OPTIONS']['context_processors'] `` list. See the
`Django documentation`_ for details.
.. _`Django documentation`: https://docs.djangoproject.com/en/1.11/ref/templates/upgrading/#the-templates-settings
2017-10-18 01:20:20 +00:00
2014-11-25 20:58:02 +00:00
This will add the config instance to the context of any template
rendered with a `` RequestContext `` .
Then, in your template you can refer to the config values just as
any other variable, e.g.:
.. code-block :: django
<h1>Welcome on {{ config.SITE_NAME }}</h1>
{% if config.BETA_LAUNCHED %}
Woohoo! Head over <a href="/sekrit/">here</a> to use the beta.
{% else %}
Sadly we haven't launched yet, click <a href="/newsletter/">here</a>
2024-08-23 12:07:24 +00:00
to signup for our newsletter.
2014-11-25 20:58:02 +00:00
{% endif %}
2016-11-27 05:55:21 +00:00
Command Line
^^^^^^^^^^^^
2025-01-07 14:36:24 +00:00
Constance settings can be get/set on the command line with the manage command :command: `constance` .
2016-11-27 05:55:21 +00:00
Available options are:
2025-01-07 14:36:24 +00:00
.. program :: constance
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. option :: list
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
list all Constance keys and their values
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. code-block :: console
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
$ ./manage.py constance list
THE_ANSWER 42
SITE_NAME My Title
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. option :: get <KEY>
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
get the value of a Constance key
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. code-block :: console
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
$ ./manage.py constance get THE_ANSWER
42
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. option :: set <KEY> <VALUE>
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
set the value of a Constance key
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
.. code-block :: console
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
$ ./manage.py constance set SITE_NAME "Another Title"
2016-11-27 05:55:21 +00:00
2025-01-07 14:36:24 +00:00
If the value contains spaces it should be wrapped in quotes.
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
.. note :: Set values are validated as per in admin, an error will be raised if validation fails:
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
E.g., given this config as per the example app:
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
.. code-block :: python
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
CONSTANCE_CONFIG = {
...
'DATE_ESTABLISHED': (date(1972, 11, 30), "the shop's first opening"),
}
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
Setting an invalid date will fail as follow:
2017-01-11 08:52:19 +00:00
2025-01-07 14:36:24 +00:00
.. code-block :: console
2020-01-27 19:24:32 +00:00
2025-01-07 14:36:24 +00:00
$ ./manage.py constance set DATE_ESTABLISHED '1999-12-00'
CommandError: Enter a valid date.
2020-01-27 19:24:32 +00:00
2025-01-07 14:36:24 +00:00
.. note :: If the admin field is a :class: `MultiValueField` , then the separate field values need to be provided as separate arguments.
E.g., a datetime using :class: `SplitDateTimeField` :
.. code-block :: python
CONSTANCE_CONFIG = {
'DATETIME_VALUE': (datetime(2010, 8, 23, 11, 29, 24), 'time of the first commit'),
}
Then this works (and the quotes are optional):
.. code-block :: console
./manage.py constance set DATETIME_VALUE '2011-09-24' '12:30:25'
This doesn't work:
.. code-block :: console
./manage.py constance set DATETIME_VALUE '2011-09-24 12:30:25'
CommandError: Enter a list of values.
.. option :: remove_stale_keys
delete all Constance keys and their values if they are not in settings.CONSTANCE_CONFIG (stale keys)
.. code-block :: console
$ ./manage.py constance remove_stale_keys
Record is considered stale if it exists in database but absent in config.
2020-01-27 19:24:32 +00:00
2014-11-25 20:58:02 +00:00
Editing
-------
Fire up your `` admin `` and you should see a new app called `` Constance ``
with `` THE_ANSWER `` in the `` Config `` pseudo model.
2020-02-12 19:23:11 +00:00
By default, changing the settings via the admin is only allowed for superusers.
To change this, feel free to set the :setting: `CONSTANCE_SUPERUSER_ONLY`
setting to `` False `` and give users or user groups access to the
2014-11-25 20:58:02 +00:00
`` constance.change_config `` permission.
2024-06-14 14:57:30 +00:00
.. figure :: _static/screenshot1.png
2014-11-25 20:58:02 +00:00
The virtual application `` Constance `` among your regular applications.
2016-02-22 17:02:31 +00:00
Custom settings form
--------------------
If you aim at creating a custom settings form this is possible in the following
way: You can inherit from `` ConstanceAdmin `` and set the `` form `` property on
your custom admin to use your custom form. This allows you to define your own
formsets and layouts, similar to defining a custom form on a standard
Django ModelAdmin. This way you can fully style your settings form and group
settings the way you like.
.. code-block :: python
2023-04-07 13:16:39 +00:00
from constance.admin import ConstanceAdmin, Config
from constance.forms import ConstanceForm
2016-02-22 17:02:31 +00:00
class CustomConfigForm(ConstanceForm):
def __init__(self, *args, * *kwargs):
2019-12-23 21:20:41 +00:00
super().__init__(*args, * *kwargs)
2016-02-22 17:02:31 +00:00
#... do stuff to make your settings form nice ...
class ConfigAdmin(ConstanceAdmin):
2016-05-30 04:04:40 +00:00
change_list_form = CustomConfigForm
2016-02-22 17:02:31 +00:00
change_list_template = 'admin/config/settings.html'
admin.site.unregister([Config])
admin.site.register([Config], ConfigAdmin)
2017-02-14 01:20:26 +00:00
You can also override the `` get_changelist_form `` method which is called in
2017-02-14 01:28:47 +00:00
`` changelist_view `` to get the actual form used to change the settings. This
2017-02-14 01:20:26 +00:00
allows you to pick a different form according to the user that makes the
request. For example:
.. code-block :: python
class SuperuserForm(ConstanceForm):
# Do some stuff here
class MyConstanceAdmin(ConstanceAdmin):
def get_changelist_form(self, request):
if request.user.is_superuser:
return SuperuserForm:
else:
2019-12-23 21:20:41 +00:00
return super().get_changelist_form(request)
2017-02-14 01:20:26 +00:00
Note that the default method returns `` self.change_list_form `` .
2016-02-22 17:02:31 +00:00
2014-11-25 20:58:02 +00:00
More documentation
------------------
.. toctree ::
:maxdepth: 2
backends
2015-06-02 19:00:03 +00:00
testing
2014-11-25 20:58:02 +00:00
changes
Indices and tables
==================
* :ref: `genindex`
* :ref: `modindex`
* :ref: `search`