Skip to main content

Settings for packages.

Project description

WebCase Settings

A flexible Django settings framework for defining, validating, and managing application-wide and per-user settings using Pydantic DTOs, Django ORM, and lazy resolvers.

Installation

pip install wc-django-settings

In settings.py:

INSTALLED_APPS += [
  'wcd_settings',
]

MIDDLEWARE += [
  'wcd_settings.middleware.settings_middleware',
]

Usage

1. Defining and Registering Settings

1.1 Define DTOs (Pydantic Models)

Create Pydantic models to define the shape and validation of your settings.

# myapp/dtos.py
from wcd_settings.registries import SettingsDTO


class ThemeSettings(SettingsDTO):
    color_scheme: str = Field(default='light', description='Theme color scheme')
    font_size: int = Field(default=14, ge=8, le=32, description='Font size in px')


class NotificationsSettings(SettingsDTO):
    enabled: bool = Field(default=True, description='Enable notifications')
    frequency: str = Field(default='daily', description='Notification frequency')

1.2 Register DTOs

Register these models with app_settings_registry (for global settings) or user_settings_registry (for per-user settings).

# myapp/registry.py
from wcd_settings.registries import (
  AppSettingsDescriptor, UserSettingsDescriptor,
  app_settings_registry, user_settings_registry,
)
from .dtos import ThemeSettings, NotificationsSettings


# Application-wide setting
app_settings_registry.register(
  AppSettingsDescriptor(
    # Unique key
    key='theme',
    # DTO model to validate settings
    dto=ThemeSettings,
    # Optional json schema object that describes DTO
    schema=ThemeSettings.model_json_schema(),
    # Defines whether this object could be edited through HTTP API
    is_editable=False,
    # Defines whether this object could be accessed through HTTP API
    is_readable=True,
  )
)

# User-specific setting
user_settings_registry.register(
  UserSettingsDescriptor(
    key='notifications', dto=NotificationsSettings,
    schema=NotificationsSettings.model_json_schema(),
    is_editable=True, is_readable=True,
  )
)

2. Fetching Settings with Resolvers

Resolvers provide lazy-loading and caching of settings from the database.

from wcd_settings import shortcuts

# App Settings
# Optional request parameter to use middleware-injected settings and cahe
app_settings = shortcuts.app_settings(request=None)
theme: Optional[ThemeSettings] = app_settings.resolver.get('theme')

print(theme.color_scheme)  # "light" by default or DB value

# User Settings
user_settings = shortcuts.user_settings(request=None)
# Getting settings for user_id=42
notifications: Optional[NotificationsSettings] = user_settings.resolver.get((42, 'notifications'))

if notifications.enabled:
    print('User has notifications enabled')

Or without shortcuts:

from wcd_settings.resolvers import (
    make_app_resolver, make_user_resolver,
)
from wcd_settings.setters import (
    make_app_setter, make_user_setter,
)


app_resolver = make_app_resolver()
user_resolver = make_user_resolver()

# You may use resolvers separately:
theme: Optional[ThemeSettings] = app_resolver.get('theme')
notifications: Optional[NotificationsSettings] = user_resolver.get((42, 'notifications'))

# And them create setters based on them:
app_settings = make_app_setter(app_resolver)
user_settings = make_user_setter(user_resolver)

3. Updating Settings with Setters

Setters update settings in the database with full Pydantic validation.

3.1 Application Settings Setter

from wcd_settings import shortcuts
from myapp.dtos import ThemeSettings


app_settings = shortcuts.app_settings(request=None)

# Update theme settings
theme_dto = ThemeSettings(color_scheme='dark', font_size=16)
app_settings.save('theme', theme_dto)

3.2 User Settings Setter

from wcd_settings.setters import make_user_setter
from myapp.dtos import NotificationsSettings


user_settings = shortcuts.user_settings(request=None)

# Update user notifications settings
user_id = 42
notif_dto = NotificationsSettings(enabled=False, frequency='weekly')
user_settings.save((user_id, 'notifications'), notif_dto)

4 Access Settings in Views

Use the request.app_settings and request.user_settings properties injected by middleware.

# views.py
from django.http import JsonResponse

def profile_view(request):
    theme = request.app_settings.resolver.get('theme')
    notifications = request.user_settings.resolver.get((request.user.id, 'notifications'))

    return JsonResponse({
        'theme': theme.color_scheme,
        'notifications_enabled': notifications.enabled,
    })

5. Advanced Usage

5.1 Partial Updates

You can update nested fields within a settings DTO:

from wcd_settings.setters import AppSettingsSetAction


actions = [
    AppSettingsSetAction(key='theme', path=['font_size'], value=18),
    AppSettingsSetAction(key='theme', path=['color_scheme'], value='dark'),
]

setter = make_app_setter(make_app_resolver())
commit, failures = setter.set(actions)

if not failures:
    commit()  # Save changes

You may even inject values into more complex datastructures

from wcd_settings.setters import UserSettingsSetAction
from wcd_settings.registries import (
  SettingsDTO, UserSettingsDescriptor,
  user_settings_registry,
)
from wcd_settings.resolvers import make_user_resolver
from wcd_settings.setters import make_user_setter


class Nested(SettingsDTO):
    value: List[Optional[str]] = Field(default_factory=list)


class Complex(SettingsDTO):
    title: Optional[str] = None
    nested: Nested = Field(default_factory=Nested)


# User-specific setting
user_settings_registry.register(
  UserSettingsDescriptor(
    key='complex', dto=Complex,
    schema=Complex.model_json_schema(),
    is_editable=True, is_readable=True,
  )
)

actions = [
    UserSettingsSetAction((1, 'complex'), path=['title'], value='Title'),
    UserSettingsSetAction(
      (1, 'complex'),
      # Path could also inject values by list indexes:
      path=['nested', 'value', 0],
      value='first',
    ),
    UserSettingsSetAction(
      (1, 'complex'),
      # Even if index is larger than current data length all the previous
      # items in the list will be filled with `None`'s.
      # Be aware, that DTO validation should also support that.
      path=['nested', 'value', 3],
      value='fourth',
    ),
]

setter = make_user_setter(make_user_resolver())
commit, failures = setter.set(actions)

if not failures:
    commit()  # Save changes

6. Views

# urls.py
from wcd_settings.urls import make_urlpatterns


# This will add views for getting and editing app and user settings:
urlpatterns += make_urlpatterns()

After that you may call:

  • /api/v1/settings/app/all/ - Retrieving all the app settings.
  • /api/v1/settings/app/set/ - Setting the values for the app settings.
  • /api/v1/settings/own/all/ - Retrieving all the current user's settings.
  • /api/v1/settings/own/set/ - Setting the values for the current user's settings.

From the example above calling the /api/v1/settings/own/set/ with:

{
  "actions": [
    {"path": ["complex", "title"], "value": "Title"},
    {"path": ["complex", "nested", "value", 0], "value": "one"},
  ]
}

Will update the user's settings to the following:

settings = Complex(
  title='Title',
  nested=Nested(
    value=['one'],
  ),
)

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.1.2]

Getters and setters are fully functional.

[0.1.0]

Initial version.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wc_django_settings-0.1.2.tar.gz (36.2 kB view details)

Uploaded Source

File details

Details for the file wc_django_settings-0.1.2.tar.gz.

File metadata

  • Download URL: wc_django_settings-0.1.2.tar.gz
  • Upload date:
  • Size: 36.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for wc_django_settings-0.1.2.tar.gz
Algorithm Hash digest
SHA256 dd5c9d87616ccd1dba8b7cc87225c99a834247d5d05a5343bc5cbcc56da44bf8
MD5 7d2d6969146f3230fe5195399106ccba
BLAKE2b-256 d3c4f47e30b1f884c57bc9190af2ae8c9d88ba7cbaf0db8d61043575b34fc95f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page