Skip to main content

django-features

A collection of fearures used in our Django-based web applications

Changelog

Installation

pip install ftw-django-features

Usage

Add desired app to INSTALLED_APPS in your Django project.

Available apps:

django_features.system_message
django_features.custom_fields

Each app includes its own translation catalogs, which Django discovers through INSTALLED_APPS. No package-specific LOCALE_PATHS entry is needed.

If you use the shared serializer fields in django_features.fields, also add django_features to INSTALLED_APPS to load their translations. The two feature apps do not require this root app.

Django merges installed apps' translation catalogs; translations are not scoped to the app that provides them. In German, the custom-fields catalog translates the context-free message Label as Bezeichnung, which can also affect the same message in other apps. To override it, provide a translation in a project catalog in LOCALE_PATHS or in an app listed before django_features.custom_fields in INSTALLED_APPS.

Configuration

If you want to use django_features, your base configuration class should inherit from django_features.settings.BaseConfiguration.

from django_features.settings import BaseConfiguration


class Base(BaseConfiguration):
    ...

Custom Fields

To use all features of the django_features.custom_fields app, the following steps are required:

Add the django_features.custom_fields.routers.custom_field_router to your ROOT_URLCONF. For example:

path("api/", include(custom_field_router.urls)),

Create your own custom field and value models

  1. You need to create a custom field model and a custom value model.
  2. Your custom field model should inherit from django_features.custom_fields.models.field.AbstractBaseCustomField.
  3. Your custom value model should inherit from django_features.custom_fields.models.value.AbstractBaseCustomValue.

Configuration

  • You can configure the models used by the django_features.custom_fields app by setting the CUSTOM_FIELD_MODEL or CUSTOM_FIELD_VALUE_MODEL setting.
  • The swapped models should inherit from django_features.custom_fields.models.field.AbstractBaseCustomField or django_features.custom_fields.models.value.AbstractBaseCustomValue.

Models with custom values

  1. Your models with custom values should inherit from django_features.custom_fields.models.CustomFieldBaseModel.
  2. Your models should have a relation to the custom value model. For example:
    • custom_values = models.ManyToManyField(blank=True, to=CustomValue, verbose_name=_("Benutzerdefinierte Werte"))

Querysets

Your querysets for the models with custom values should inherit from django_features.custom_fields.models.CustomFieldModelBaseManager.

Serializers

Your serializers for the models with custom values should inherit from django_features.custom_fields.serializers.CustomFieldBaseModelSerializer.

System Message

If you want to use django_features.system_message, your base configuration class should inherit from django_features.system_message.settings.SystemMessageConfigurationMixin.

Then call the super property:

@property
def CONSTANCE_CONFIG(self) -> dict:
    config = super().CONSTANCE_CONFIG
    return {**config, ...}

@property
def CONSTANCE_CONFIG_FIELDSETS(self) -> dict:
    config = super().CONSTANCE_CONFIG_FIELDSETS
    return {
        **config,
        ...
    }

Add the django_features.system_message.routers.system_message_router to your ROOT_URLCONF. For example:

path("api/", include(system_message_router.urls)),

Development

Installing dependencies, assuming you have poetry installed:

poetry install

Translations

Catalogs live in each feature app's locale/ directory. Shared serializer-field messages remain in django_features/locale/, and demo-only messages live in app/locale/. Keep translations of messages shared between apps consistent.

From the repository root, update the catalogs with:

poetry run ./bin/i18n_update

This requires GNU gettext. Edit the German, English, and French .po files as needed, then run the command again to compile them. Commit both .po and .mo files; the compiled package catalogs are included in the distribution so consumers do not need to run compilemessages.

Release

This package uses towncrier to manage the changelog, and to introduce new changes, a file with a concise title and a brief explanation of what the change accomplishes should be created in the changes directory, with a suffix indicating whether the change is a feature, bugfix, or other.

To make a release and publish it to PyPI, the following command can be executed:

./bin/release

This script utilizes zest.releaser and towncrier to create the release, build the wheel, and publish it to PyPI.

Before running the release command, it is necessary to configure poetry with an access token for PyPI by executing the following command and inserting the token stored in 1password:

poetry config pypi-token.pypi <token>

The version attribute in the pyproject.toml file should be updated to the new version before running the release command, because this version will be published to PyPI.

Custom-field upgrade

AbstractBaseCustomValue provides the optional, untranslated external_key field. It stores the stable token supplied by the external system, independently of the display label and application value. It replaces external_label from 2026.5.0. Every concrete subclass upgrading from that release needs a RenameField migration, plus removal and recreation of any constraint that names the old field. The example app's 0003_rename_external_choice_key demonstrates a reversible upgrade that preserves existing keys and values. New consumers need an AddField migration. Apply consumer migrations before starting application workers with the upgraded package; do not add this field to modeltranslation.

If non-empty external keys must be unique within a field, add this constraint to the concrete value model's Meta.constraints before generating migrations, using a unique constraint name:

from django.db import models

models.UniqueConstraint(
    fields=["field", "external_key"],
    condition=~models.Q(external_key=""),
    name="custom_value_field_external_key_unique",
)

Matching choices

For a choice field field, match the external system's token directly against the choice's exact external_key, without language or matching configuration:

from django_features.custom_fields.matching import ChoiceMatcher

matcher = ChoiceMatcher(field.choices)
choice = matcher.resolve(external_token)

Generic callers can still use attribute="value", or attribute="label" with an explicit configured language. When loading several fields, use the custom-field queryset's with_choices() to prefetch their choices.

Matching skips None and empty-string keys after normalization. Duplicate keys raise an ambiguous-match validation error only when resolved; unique keys remain matchable. Empty or unknown keys raise a missing-match validation error.

Release files for ftw-django-features 2026.5.1

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

Source distribution (sdist)

Source distribution for ftw-django-features 2026.5.1
File Size Uploaded
ftw_django_features-2026.5.1.tar.gz 33.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ftw-django-features 2026.5.1
File Interpreter ABI Platform
ftw_django_features-2026.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 93.1 kB

Release files / ftw_django_features-2026.5.1.tar.gz

Download URL ftw_django_features-2026.5.1.tar.gz
Size 33.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5ccedb326124eba87b42c05eeafbee9d850c9197d3297e8786d7ccd21892f56a
BLAKE2b-256 checksum
How to use checksums
7416709385fc114cca1ebcb554d4abdeb88f2b062fdd1980d25ce75c972208b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.0 CPython/3.13.13 Darwin/24.6.0

Release files / ftw_django_features-2026.5.1-py3-none-any.whl

Download URL ftw_django_features-2026.5.1-py3-none-any.whl
Size 60.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c5f2f3136ebdc4275d85a72879bb9d55704afddcdf019299d6edc5006010648d
BLAKE2b-256 checksum
How to use checksums
3cbcf1bd58f6257b49ac5cfa08e2857e091dedad20f3dfc1816ddf72bad8caa6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.0 CPython/3.13.13 Darwin/24.6.0
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