Skip to main content

Django DataclassConf

CI PyPI - Version License: MIT

A simple Django package setting loader that utilizes dataclasses for type hinting and type checking.

Bring modern Python typing, robust validation, and full IDE autocomplete to your Django configurations.

django-dataclassconf allows you to bind your Django settings cleanly to structured standard Python dataclasses. Powered by dacite, it automatically catches dynamic setting updates while ensuring your configuration layer stays type-safe and isolated.

But Why?

  • Fail-Fast Validation: Catch bad configuration types or missing values instantly during server startup or container deployment rather than hitting silent runtime crashes mid-request.

  • Full IDE Autocomplete: Say goodbye to blind getattr(settings, "MY_SETTING") calls. Enjoy full hovering type definitions and autocompletion in VS Code, PyCharm, and MyPy.

  • Dual Format Normalization: Merges flat environment styles (PREFIX_TIMEOUT = 30) and structured dictionary blocks (PREFIX = {"TIMEOUT": 30}) into a single unified object seamlessly.

  • Test-Safe Isolation: Fully supports Django's test suite cycles. When settings are overridden dynamically in unit tests, your dataclasses mutate cleanly in-place and revert automatically.

Usage

1. Define Your Configuration Dataclass

Create a file named config.py (or any name you prefer tbh) within your application or package. Inherit from BaseConfig and define your variables.

from dataclasses import dataclass
from django_dataclassconf.conf import BaseConfig, config_loader

@dataclass
class MyPackageConfig(BaseConfig):
    DOCUMENTS_ROOT_PATH: str = '/the/default/path/'
    MAX_FILE_SIZE: int = 10000

    _prefix = 'MY_PACKAGE'

package_config = MyPackageConfig()

Note: Make sure to instantiate your config class for it to properly catch the settings and you can then access those settings via its instance.

2. Subscribe to the Configuration Loader

For your dataclass to grab configuration data from Django's settings.py on startup and capture updates during tests, subscribe your instance into config_loader inside your app's initialization hook:

# my_app/apps.py
class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        from django_dataclassconf.conf import config_loader
        from .config import package_config

        config_loader.subscribe(package_config)

3. Access Your Settings Anywhere

The core practice is to import your configuration instance directly instead of using the global django.conf.settings object, to get full type safety and IDE autocomplete.

# my_app/views.py
from django.http import HttpResponse
from my_app.config import package_config

def my_view(request):
    ...
    # Your IDE now natively autocompletes these fields
    if file_size > package_config.MAX_FILE_SIZE:
        return HttpResponse(
            {'detail': 'File size has exceeded the maximum size limit!'}, 
            status = 400
        )

Extras

Nested Dataclasses

Your configuration dataclass can also contain nested dataclasses. You only need to inherit BaseConfig on the root configuration dataclass.

from dataclasses import dataclass, field
from django_dataclassconf.conf import BaseConfig, config_loader

@dataclass
class DocumentPreview:
    preview_page_count: int = 10
    strip_cover_page: bool = False

@dataclass
class MyPackageConfig(BaseConfig):
    DOCUMENTS_ROOT_PATH: str = '/the/default/path/'
    PREVIEW: DocumentPreview = field(default_factory=DocumentPreview)

    _prefix = 'MY_PACKAGE'

configuration = MyPackageConfig()
config_loader.subscribe(configuration)

Which maps to this in settings.py:

MY_PACKAGE = {
    'DOCUMENTS_ROOT_PATH': '/custom/path/',
    'PREVIEW': {
        'preview_page_count': 12,
        'strip_cover_page': True
    },
}

Built-in Field Types

Importable

For settings that hold a dotted import path to a class, instance, or callable in another module, annotate them with Importable from fields/types.py. The import is deferred — nothing is resolved until you explicitly call .resolve().

from dataclasses import dataclass
from django_dataclassconf.fields import Importable
import typing

from .models import Segment

@dataclass
class MyConfig(BaseConfig):
    SEGMENTER_FUNC: Importable[typing.Callable] = 'myapp.utils.segment_audio'
    SEGMENT_SERIALIZER_CLASS: Importable[typing.Type[Serializer]] = 'myapp.serializers.SegmentSerializer'
    SEGMENT_MODEL: Importable[typing.Type[Segment]] = 'myapp.Segment'

    _prefix = 'MY_PACKAGE'

configuration = MyConfig()

Call .resolve() when you need the actual object. It validates the type on first call and caches the result:

# Raises ImportError if the path is invalid, or TypeError if the
# resolved object does not match the annotated type.
try:
    segment_model = configuration.SEGMENT_MODEL.resolve()

except ImportError as ie:
    print(f'Could not import SEGMENT_MODEL: {ie}')

except TypeError as te:
    print(f'Imported value has different type than expected: {te}')

Path

For settings that hold a filesystem path, annotate them with Path from fields/types.py. The raw value may be a str or any os.PathLike; calling .resolve() validates it and returns a pathlib.Path.

from dataclasses import dataclass
from django_dataclassconf.fields import Path

@dataclass
class MyConfig(BaseConfig):
    DOCUMENTS_ROOT: Path[str] = '/the/default/path/'

    _prefix = 'MY_PACKAGE'

configuration = MyConfig()

Call .resolve() to get the pathlib.Path:

# Returns pathlib.Path('/the/default/path/')
root = configuration.DOCUMENTS_ROOT.resolve()

document = root / 'report.pdf'

Note: Path intentionally shares its name with pathlib.Path. Import it under an alias (e.g. from django_dataclassconf.fields import Path as PathField) if you also need pathlib.Path in the same module.

Custom Field Types

Introduced in version 0.3.0.

You can define your own field types with custom validation and transformation logic by two ways.

Decorator

Introduced in version 0.5.0, a simple way to customize a field type is by using a builder function:

  • field_type — a decorator that automatically subclasses FieldGeneric and Field.
  • create_field_type — a function that performs the same thing as field_type.

Subclasses

Introduced in version 0.3.0, by subclassing three base classes from fields/bases.py: FieldValue, FieldGeneric, and Field.

Each custom field type requires three pieces:

  • FieldValue subclass — holds the raw value, implements validate() (raises on invalid input) and resolve() (returns the final value).
  • FieldGeneric subclass — the runtime object produced by YourField[T]. Implements instanciate() to construct the FieldValue.
  • Field subclass — the annotation class used in dataclass definitions. Points at the FieldGeneric via _generic_class.

Simple Example: Email Field (Decorator)

The simplified way of defining your own field type is by using the field_type decorator in fields/decorators.py.

from django_dataclassconf.fields import field_type, FieldValue
import re

@field_type()
class Email(FieldValue):
    def validate(self):
        email_pattern = r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'
        if not re.match(email_pattern, self.value):
            raise ValueError(f'Email value {self.value} is not a valid email address')

    def __str__(self):
        return self.value

    def __eq__(self, value):
        if isinstance(value, str):
            return self.value == value
        return super().__eq__(value)

# Email here serves as FieldValue subclass on static checkers but
# it is a subclass of Field during runtime

Alternatively, you may also use the builder function that the decorator uses internally which is create_field_type function located in fields/utils.py.

from django_dataclassconf.fields import FieldValue, create_field_type
import re

class EmailValue(FieldValue):
    ... # implementation here

Email = create_field_type(EmailValue, 'Email')

# EmailValue here stays as FieldValue subclass and
# Email is a subclass of Field

Use it in your configuration dataclass the same way as any built-in field type:

@dataclass
class MyConfig(BaseConfig):
    ADMIN_EMAIL: Email[str] = 'admin@example.com'

    _prefix = 'MY_APP'

Call .resolve() to get the validated value, or check .is_valid if you want a boolean without raising:

# Raises ValueError if the configured value is not a valid email address
admin_email = my_config.ADMIN_EMAIL.resolve()

# Non-raising check
if my_config.ADMIN_EMAIL.is_valid:
    ...

Advanced Example: Deprecated Field (Subclass)

A more controlled way to create your custom field type is by subclassing the classes FieldValue, FieldGeneric, and Field. An example will follow which is a field that emits a DeprecationWarning when validated, useful for marking settings that are still supported but scheduled for removal.

from django_dataclassconf.fields import Field, FieldGeneric, FieldValue
import warnings
import typing


class DeprecatedValue(FieldValue):
    def validate(self):
        if not isinstance(self.value, self.inner_type):
            raise TypeError(
                f'Instance {self.value} is not of type {self.inner_type.__name__}'
            )
        warnings.warn('This setting is deprecated', DeprecationWarning, stacklevel=2)

# The following subclasses can be customized as much as you like
class _DeprecatedGeneric(FieldGeneric):
    _value_class = DeprecatedValue

    def instantiate(self, value):
        # you may add anything here
        return super().instantiate(value)

class _DeprecatedField(Field):
    _generic_class = _DeprecatedGeneric

if typing.TYPE_CHECKING:
    Deprecated = DeprecatedValue

else:
    Deprecated = _DeprecatedField

Note: A convention in naming your custom fields would to name ObjValue for subclass of FieldValue, _ObjGeneric for subclass of FieldGeneric, and _ObjField for subclass of Field. When using the decorator field_type, you may name your FieldValue subclass without the ..Value suffix. When using the builder function create_field_type, add the ..Value suffix on the subclass of FieldValue.

License

This project is licensed under the MIT License — see the LICENSE file for details.

Metadata

Release files for django-dataclassconf 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-dataclassconf 0.5.0
File Size Uploaded Provenance Actions
django_dataclassconf-0.5.0.tar.gz 17.2 kB With provenance Details for django_dataclassconf-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-dataclassconf 0.5.0
File Interpreter ABI Platform Actions
django_dataclassconf-0.5.0-py3-none-any.whl Python 3 none any Details for django_dataclassconf-0.5.0-py3-none-any.whl

Total release size: 33.0 kB

Release files / django_dataclassconf-0.5.0.tar.gz

Download URL django_dataclassconf-0.5.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
da6a116e3946da6ec9f950a08e32d74a742788847a6f38400392b1b36ebc9370
BLAKE2b-256 checksum
How to use checksums
9a26dba39f90ead44c33f7d63bf29baae28d92c6ff669a497df7d80b272401ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release files / django_dataclassconf-0.5.0-py3-none-any.whl

Download URL django_dataclassconf-0.5.0-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5612b19884985c4cb9db64becbdbb19c2ee8272a56581e02bed507811df91fdc
BLAKE2b-256 checksum
How to use checksums
209f4c6ca1a9ee3bd1612973451b4c7f60c58b87fd385307009ada310eec42c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Security for django-dataclassconf 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_dataclassconf-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page