Skip to main content

Django FeatureVault

PyPI PyPI - Python Version PyPI - License

Feature Flag Framework for Django.

Installation

python -m pip install django-featurevault

First, add django_featurevault to your INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "django_featurevault",
]

Next, add FeatureContextMiddleware to your MIDDLEWARE list:

MIDDLEWARE = [
    # ...
    "django_featurevault.middleware.FeatureContextMiddleware",
]

Finally, configure your feature flags in settings.py:

FEATURE_FLAGS = {
    "default": {
        "BACKEND": "django_featurevault.backends.settings.SettingsBackend",
        "OPTIONS": {
            "FLAGS": {
                "GLOBAL_BANNER": True,
                "STAFF_DASHBOARD": {
                    "enabled": True,
                    "conditions": {
                        "groups": [
                            {
                                "properties": [
                                    {"key": "is_staff", "operator": "exact", "value": True}
                                ]
                            }
                        ]
                    },
                },
                "NEW_CHECKOUT": {
                    "enabled": True,
                    "conditions": {
                        "groups": [
                            {"rollout_percentage": 50}
                        ]
                    },
                },
            }
        },
    }
}

Backends

Few backends are included by default:

  • django_featurevault.backends.settings.SettingsBackend: Reads feature flags directly from settings.FEATURE_FLAGS.
  • django_featurevault.backends.dummy.DummyBackend: In-memory backend for testing.

Custom Backends

You can create custom storage backends by subclassing BaseFeatureBackend and implementing get_feature and get_all_features:

from typing import Any
from django_featurevault.backends.base import BaseFeatureBackend


class CustomRedisBackend(BaseFeatureBackend):
    def __init__(self, alias: str = "default", **options: Any) -> None:
        super().__init__(alias=alias, **options)
        # Initialize client connections using options passed from settings
        self.redis_url = options.get("URL", "redis://localhost:6379/0")

    def get_feature(self, feature_name: str, default: Any = False) -> dict[str, Any]:
        """
        Fetch feature configuration dictionary by name.
        Must return a dict containing at minimum:
        {"enabled": bool, "conditions": dict}
        """
        # Fetch from your storage engine...
        return {
            "enabled": True,
            "conditions": {},
        }

    def get_all_features(self) -> dict[str, dict[str, Any]]:
        """Fetch all feature definitions for bulk evaluation."""
        return {}

Point to your custom backend in settings.py:

FEATURE_FLAGS = {
    "default": {
        "BACKEND": "my_app.backends.CustomRedisBackend",
        "OPTIONS": {
            "URL": "redis://127.0.0.1:6379/1",
        },
    }
}

Usage

Checking flags in views

Import feature and call is_enabled:

from django.shortcuts import render
from django_featurevault import feature


def home_view(request):
    if feature.is_enabled("NEW_CHECKOUT"):
        return render(request, "new_checkout.html")
    return render(request, "old_checkout.html")

If a flag is not defined, is_enabled returns False by default. You can change this using the default parameter:

feature.is_enabled("UNKNOWN_FLAG", default=True)

Passing explicit context

You can pass a custom context dictionary directly into is_enabled:

feature.is_enabled("BETA_FEATURE", context={"plan": "enterprise", "country": "IN"})

Background tasks and testing context

To evaluate feature flags in Celery workers, cron jobs, or tests where no HTTP request exists, use the context manager:

from django_featurevault import feature

with feature.context(user_id="user_101", is_staff=True):
    if feature.is_enabled("STAFF_DASHBOARD"):
        ...

Conditions and Targeting

Flags defined as dictionaries support targeting rules via conditions.

"FEATURE_NAME": {
    "enabled": True,
    "conditions": {
        "groups": [
            # Group 1: Enabled for internal staff
            {
                "properties": [
                    {"key": "is_staff", "operator": "exact", "value": True}
                ],
            }
            # OR Group 2: Enabled for 20% of beta users in India
            {
                "properties": [
                    {"key": "country", "operator": "exact", "value": "IN"},
                    {"key": "plan", "operator": "exact", "value": "beta"}
                ],
                "rollout_percentage": 20,
            }
        ]
    },
}
  • groups: Evaluated with OR logic (if any group matches, the flag is enabled).
  • properties within a group: Evaluated with AND logic (all properties in the group must match).
  • rollout_percentage: A percentage between 0 and 100 that uses sticky hashing against the user or device ID.

Supported operators

  • exact: Matches exact equality (==).
  • is_not: Matches inequality (!=).
  • in: Checks values in a list (value in [...]).
  • icontains: Case-insensitive substring match.

Automatically resolved user fields

When evaluating against an authenticated Django user, the following context keys are resolved automatically:

  • user_id, id, pk: The user's primary key (user.pk).
  • username: The user's username (getattr(user, user.USERNAME_FIELD)).
  • django_group: Group names the user belongs to (user.groups.values_list("name", flat=True)).
  • Any standard or custom attribute on the user model (like is_staff, is_superuser, email).

Cookie Configuration

FeatureContextMiddleware automatically sets an anonymous device cookie for sticky rollouts when users are not logged in.

You can customize the cookie name and options in settings.py:

FEATURE_FLAGS = {
    "CLIENT_ID_COOKIE": "ff_client_id",
    "COOKIE_OPTIONS": {
        "max_age": 30 * 24 * 60 * 60,
        "httponly": True,
        "samesite": "Lax",
    },
}

API Endpoint

To expose evaluated feature flags to frontend clients as JSON, include the URLs in your urls.py:

from django.urls import include, path

urlpatterns = [
    # ...
    path("features/", include("django_featurevault.urls")),
]

This registers GET /features/api/flags/, and returns a JSON map of all evaluated flags:

{
  "GLOBAL_BANNER": {"enabled": true},
  "STAFF_DASHBOARD": {"enabled": false},
  "NEW_CHECKOUT": {"enabled": true}
}

Example Project

An example project is included in the example/ directory.

Metadata

Release files for django-featurevault 0.1.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-featurevault 0.1.0
File Size Uploaded Provenance
django_featurevault-0.1.0.tar.gz 14.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-featurevault 0.1.0
File Interpreter ABI Platform
django_featurevault-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.4 kB

Release files / django_featurevault-0.1.0.tar.gz

Download URL django_featurevault-0.1.0.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c03c5128fec5c68e298c57d79093592d16544bd8bba5940cde27252b03f414c9
BLAKE2b-256 checksum
How to use checksums
bf7898c861e60e50130fd1648c09b68ce2382e3a3f67d3fa959a0f87611ed680
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / django_featurevault-0.1.0-py3-none-any.whl

Download URL django_featurevault-0.1.0-py3-none-any.whl
Size 13.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
38f87de89169f94e8c81d79d65de42785b6935045da74c61ede6c10e5fdd3d31
BLAKE2b-256 checksum
How to use checksums
50acbda0d0f54aff42e291979c71b036ddcc40491e28252ee0ea09fbcdcccbd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for django-featurevault 0.1.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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