django-constance

repository·master·Indexed 23 days ago

https://github.com/jazzband/django-constance

A Django app for managing dynamic settings that can be updated at runtime. It supports multiple backends including Redis, the Django database, and memory, and provides a built-in integration with the Django admin interface. Features include a management CLI for retrieving and updating keys, support for custom field types, async access methods, and a `config_updated` signal for configuration changes.

Tokens
6.4K
Snippets
22
Records
27
Agent score
83%

What's inside django-constance

  1. Overview of Constance

    master

    Constance is a Django application designed for storing dynamic settings. It allows you to manage configuration values that can be changed at runtime without redeploying your application. It supports pluggable backends, specifically providing built-in support for:

    • Redis: For high-performance, distributed settings.
    • Django model backend: For storing settings directly in your database.

    Additionally, Constance integrates with the Django admin app, providing a user interface to manage these dynamic settings.

  2. Configure Constance backends

    master

    Constance uses backends to store configuration values, which are automatically serialized/deserialized as JSON. You can override the default backend by setting the CONSTANCE_BACKEND setting to the appropriate dotted path. Available backends include Redis, Database, and Memory.

    CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend'
  3. Use override_config as a context manager or decorator in pytest

    master

    If you prefer using override_config as a standard Python context manager or decorator within your pytest tests (instead of using the marker), import it from constance.test.pytest.

    from constance.test.pytest import override_config
    
    def test_override_context_manager():
        with override_config(BOOL_VALUE=False):
            ...
    
    @override_config(BOOL_VALUE=False)
    def test_override_context_manager_decorator():
        ...
  4. Configure django-constance in settings.py

    master

    To set up Constance, follow these steps in your settings.py:

    1. Add 'constance' to your INSTALLED_APPS. It is recommended to add it before your project apps and any admin extensions like Grapelli.
    2. Define CONSTANCE_CONFIG, a dictionary where each key is a setting name. The value is a tuple containing:
      • The default value.
      • A help text string for the Django admin.
      • (Optional) A field type or a custom field label.

    If you encounter hash verification errors across different application instances, you can skip verification using CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True.

    INSTALLED_APPS = (
        'django.contrib.admin',
        'django.contrib.staticfiles',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        ...
        'constance',
    )
    
    CONSTANCE_CONFIG = {
        'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' 
                       'The Universe, and Everything'),
    }
    
    # To skip hash verification if needed
    CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True
  5. Setup the Database backend

    master

    The Database backend stores configuration values in a standard Django model.

    1. Set the backend in your settings:
    CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
    1. Run migrations:
    python manage.py migrate

    Note for upgrades: If you are upgrading Constance to 1.0 and using Django 1.7+, you may need to fake the migration if the tables already exist:

    python manage.py migrate database --fake

    Prefixing: You can set an optional prefix for database interactions using CONSTANCE_DATABASE_PREFIX (defaults to an empty string '').

    CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
    
    # Optional prefix
    CONSTANCE_DATABASE_PREFIX = 'constance:myproject:'
  6. Access Constance settings in Django templates

    master

    There are two ways to access Constance settings in templates:

    1. Manual Context: Pass the config object directly to the template context in your view.
    2. Context Processor (Recommended): Add 'constance.context_processors.config' to the top of your TEMPLATES['OPTIONS']['context_processors'] list in settings.py. This makes config available in any template rendered with a RequestContext.

    Once available, access variables using standard Django template syntax.

    # Manual approach in view
    from django.shortcuts import render
    from constance import config
    
    def myview(request):
        return render(request, 'my_template.html', {'config': config})
    {# In template #}
    <h1>Welcome on {{ config.SITE_NAME }}</h1>
    {% if config.BETA_LAUNCHED %}
        <p>Beta is live!</p>
    {% endif %}
  7. Setup the Redis backend

    master

    The Redis backend stores configuration values in a Redis store using the redis-py library.

    To install the necessary dependencies, run:

    pip install django-constance[redis]

    Standard Redis Backend

    By default, it uses constance.backends.redisd.RedisBackend, which retrieves values from Redis on every access.

    Caching Redis Backend

    constance.backends.redisd.CachingRedisBackend stores values in local memory upon first access and checks a TTL (Time To Live) on subsequent accesses to reduce Redis load.

    CONSTANCE_BACKEND = 'constance.backends.redisd.CachingRedisBackend'
    # Optionally set the TTL in seconds
    CONSTANCE_REDIS_CACHE_TIMEOUT = 60
    CONSTANCE_BACKEND = 'constance.backends.redisd.CachingRedisBackend'
    # optionally set a value ttl
    CONSTANCE_REDIS_CACHE_TIMEOUT = 60
  8. Test config overrides with override_config in Django TestCase

    master

    To test how your application behaves with different configuration values in Django, use the override_config class. It functions similarly to Django's override_settings and can be used as a class decorator, a method decorator, or a context manager.

    from constance import config
    from constance.test import override_config
    from django.test import TestCase
    
    @override_config(YOUR_NAME="Arthur of Camelot")
    class ExampleTestCase(TestCase):
    
        def test_what_is_your_name(self):
            self.assertEqual(config.YOUR_NAME, "Arthur of Camelot")
    
        @override_config(YOUR_QUEST="To find the Holy Grail")
        def test_what_is_your_quest(self):
            self.assertEqual(config.YOUR_QUEST, "To find the Holy Grail")
    
        def test_what_is_your_favourite_color(self):
            with override_config(YOUR_FAVOURITE_COLOR="Blue?"):
                self.assertEqual(config.YOUR_FAVOURITE_COLOR, "Blue?")
  9. Setup the Memory backend

    master

    The Memory backend stores configuration values in memory. These values do not persist between process restarts. This backend is intended primarily for development and testing. Use with caution in production.

    CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend'
    CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend'
  10. Internationalize Constance settings and fieldsets

    master

    You can use Django's gettext_lazy to translate field descriptions and fieldset headers.

    Important: When using internationalization, CONSTANCE_CONFIG_FIELDSETS must be defined as a tuple rather than a dictionary, because lazy proxy objects cannot be used as dictionary keys in settings files.

    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',)),
    )
  11. Use Constance settings asynchronously

    master

    If you are using Django's asynchronous features (like async views), you should use the await syntax to prevent blocking the event loop. Synchronous access in an async context is discouraged as it can cause performance issues, trigger RuntimeWarnings, or raise SynchronousOnlyOperation errors when using the Database backend.

    Key async methods:

    • await config.aset(key, value): Update a setting asynchronously.
    • await config.amget([key1, key2]): Bulk retrieval of multiple settings asynchronously.
    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'])
  12. Customize the Constance Admin form

    master

    To create a custom settings form in the Django Admin, inherit from ConstanceAdmin and provide a custom form via the change_list_form property. You can also override get_changelist_form to return different forms based on the request (e.g., showing a different form to superusers).

    from constance.admin import ConstanceAdmin, Config
    from constance.forms import ConstanceForm
    
    class CustomConfigForm(ConstanceForm):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # ... custom logic ...
    
    class ConfigAdmin(ConstanceAdmin):
        change_list_form = CustomConfigForm
        change_list_template = 'admin/config/settings.html'
    
    # Unregister default and register custom
    admin.site.unregister([Config])
    admin.site.register([Config], ConfigAdmin)