Skip to content

Defining Configuration ​

Your configuration schema lives in sysconfig.py files — one per Django app that needs configurable settings. This guide covers everything about defining apps, sections, and fields, plus advice on designing a schema that stays clean as your project grows.

The basic structure ​

python
# myapp/sysconfig.py
from django_sysconfig import register_config, Section, fields

@register_config("myapp")
class MyAppConfig:
    class General(Section):
        label = "General Settings"

        site_name = fields.String(
            label="Site Name",
            default="My App",
        )

Three pieces:

  1. @register_config("app_label") — registers the class under the given label. Must be snake_case. The label becomes the first segment of every config path in this app (myapp.general.site_name).
  2. Inner class extending Section — a logical grouping of related fields. The class name is automatically converted to snake_case and becomes the second path segment (PaymentSettings → myapp.payment_settings.*).
  3. Field assignments — individual configurable values using fields.<Type>. The attribute name must be snake_case. It becomes the third path segment (myapp.general.site_name).

Fields vs. the Field Class ​

Under the hood, every field in django-sysconfig is an instance of the Field class. The fields module provides pre-configured shorthands for all built-in types:

  • fields.String(...) is shorthand for Field(StringFrontendModel, ...)
  • fields.Integer(...) is shorthand for Field(IntegerFrontendModel, ...)
  • ...and so on.

Using the fields module is the recommended way to define your schema as it is more concise and avoids importing multiple FrontendModel classes.

Section options ​

AttributeTypeDefaultDescription
labelstr—Heading shown in the admin UI
sort_orderint0Display order among sections in this app (lower = first)
python
class Advanced(Section):
    label = "Advanced Settings"
    sort_order = 99

Field options ​

ParameterTypeRequiredDescription
frontend_modeltype[BaseFrontendModel]YesThe field type class — determines Python type and UI widget. Full list of field types
labelstrNoHuman-readable label shown in the admin UI
commentstrNoHelp text below the input; HTML is allowed
defaultAnyNoDefulat value for the config.
sort_orderintNoDisplay order within the section (lower = first)
validatorslist[BaseValidator]NoList of validators run before saving
on_saveCallableNoCallback invoked after a value is written to the database
**kwargs—NoExtra arguments passed through to the frontend model (e.g. choices, step)

A complete example ​

python
from django_sysconfig import register_config, Section, Field, fields, validators
from django_sysconfig.frontend_model import StringFrontendModel

@register_config("store")
class StoreConfig:
    class General(Section):
        label = "General"
        sort_order = 10

        store_name = fields.String(
            label="Store Name",
            default="My Store",
            validators=[validators.NotEmptyValidator()],
        )
        # Or the old way as:
        # store_name = Field(
        #   StringFrontendModel,
        #   label="Store Name",
        #   default="My Store",
        #   validators=[validators.NotEmptyValidator()],
        # )

        store_description = fields.Textarea(
            label="Store Description",
            comment="Shown on the About page. HTML allowed.",
            default="",
            sort_order=20,
        )

        currency = fields.Select(
            label="Currency",
            default="usd",
            choices=[("usd", "USD"), ("eur", "EUR"), ("gbp", "GBP")],
            validators=[validators.ChoiceValidator(["usd", "eur", "gbp"])],
            sort_order=30,
        )

    class Email(Section):
        label = "Email"
        sort_order = 20

        sender = fields.String(
            label="Sender Address",
            default="shop@example.com",
            validators=[validators.NotEmptyValidator(), validators.EmailValidator()],
        )

    class Payments(Section):
        label = "Payments"
        sort_order = 30

        live_mode = fields.Boolean(
            label="Live Mode",
            comment="<strong>Warning:</strong> enabling this processes real charges.",
            default=False,
        )

        tax_rate = fields.Decimal(
            label="Tax Rate",
            default="0.20",
            step="0.01",
            validators=[validators.RangeValidator(min_value=0, max_value=1)],
        )

        stripe_secret_key = fields.Secret(
            label="Stripe Secret Key",
            comment="Stored encrypted. Starts with <code>sk_</code>.",
        )

Designing your schema ​

One sysconfig.py per app ​

Each Django app should own its own configuration. Don't put all configuration for an entire project in a single file. The app label in @register_config groups fields in the admin UI and forms the first segment of every path.

Section boundaries ​

A section is a visual and semantic grouping. Good sections:

  • Have a clear, single concern ("Email Settings", "Payments", "Feature Flags")
  • Contain 3–10 fields; fewer than 3 might not need their own section, more than 10 might need splitting

Field naming ​

Field attribute names become path segments. Use snake_case. Keep names descriptive but concise — they appear in code as config.get("myapp.section.field_name").

Avoid generic names like value, data, or config that mean nothing in isolation.

Always set a default ​

Fields without a default will return None from config.get(...) until a value is explicitly saved to the database. This is usually fine for secret fields (which have no sensible default), but for most other fields, a default makes your app's behavior predictable on a fresh install.

Use comment liberally ​

The comment field is shown below the input in the admin UI. Use it to explain:

  • What the value is used for
  • What format is expected (e.g., "As a decimal, e.g. 0.20 for 20%")
  • Any side effects of changing the value
  • Links to external documentation

HTML is allowed in comment, so you can use <code>, <strong>, and links.

Sort order ​

Both sections and fields accept a sort_order integer. Lower numbers appear first. If you don't specify sort_order, fields appear in the order Python sees them (which is definition order in Python 3.7+). Being explicit with sort_order makes your schema easier to reorganize later without changing the visual order in the admin UI.

What happens on startup ​

When your sysconfig.py is imported:

  1. The @register_config(...) decorator registers the class with the global ConfigRegistry.
  2. For every field that has a default, a ConfigValue database row is created using get_or_create. If a row already exists (from a previous run), it's left untouched.
  3. Every field will have a one-one mapping in the DB row and is created on the app start or restart.

This means it's safe to add new fields with defaults between deployments — they'll be available immediately on the next startup, with no manual data migration needed.

Released under the MIT License.