Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Feature Flags Framework for Django

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages