Feature flags for Django projects
Django-Flags is an application designed specifically for Django, empowering developers to utilize feature flags to toggle functionality in both Django code and templates based on configurable conditions. It's actively maintained, with recent updates adding support for Django 6.0 and Python 3.13, and typically sees several releases per year addressing new Django/Python versions and bug fixes.
Common errors
-
ModuleNotFoundError: No module named 'flags'
cause The 'flags' app is not registered in Django's INSTALLED_APPS.fixAdd `'flags'` to your `INSTALLED_APPS` list in `settings.py`. -
TypeError: 'dict' object is not iterable
cause In `settings.FLAGS`, a flag's conditions are defined as a single dictionary instead of a list containing dictionaries, which was removed in Django-Flags 5.0.fixChange the flag definition in `settings.py` from `{'FLAG_NAME': {'condition': '...', 'value': ...}}` to `{'FLAG_NAME': [{'condition': '...', 'value': ...}]}` (a list of dictionaries). -
django.core.exceptions.ImproperlyConfigured: The 'request' context processor is required
cause The `django.template.context_processors.request` is missing from your `TEMPLATES` context processors, which is necessary for many flag conditions and template tags.fixAdd `'django.template.context_processors.request'` to the `'context_processors'` list within your `TEMPLATES` configuration in `settings.py`. -
404 Not Found (for a URL managed by flagged_path)
cause A `flagged_path` URL pattern was accessed, but the associated flag's conditions were not met and no `fallback` view was specified to handle this state.fixEither ensure the flag conditions are met, or provide a `fallback` view to `flagged_path` (e.g., `flagged_path('MY_FLAG', 'my-url/', my_view, fallback=fallback_view)`).
Warnings
- breaking As of Django-Flags 5.1.0, support for Python versions older than 3.10 has been removed. Projects using Python 3.9 or earlier must upgrade their Python environment before upgrading `django-flags`.
- breaking In Django-Flags 4.0, the `flags.template_functions.flag_enabled` and `flags.template_functions.flag_disabled` for Jinja2 templates were removed. Jinja2 integration now requires using the `flags.jinja2tags.flags` Jinja2 extension.
- breaking Django-Flags 5.0 removed support for defining settings-based flag conditions using a single dictionary. Conditions for a flag must now always be a list of dictionaries or tuples, even for a single condition.
- gotcha Without `django.template.context_processors.request` in your `TEMPLATES` options, the `request` object will not be available in templates or for certain conditions (like `user` or `parameter`) when checking flags, leading to unexpected behavior or errors.
- gotcha When using `flagged_path` or `FlaggedViewMixin` without providing a `fallback` view, accessing the URL when the flag condition is not met will result in a 404 Not Found error instead of a graceful redirect or alternative content.
Install
-
pip install django-flags
Imports
- flag_enabled
from flags.state import flag_enabled
- feature_flags (template tag)
{% load feature_flags %} - flagged_path
from flags.urls import flagged_path
- flag_required
from flags.decorators import flag_required
- conditions (module)
from flags import conditions
- FlaggedViewMixin, FlaggedTemplateView
from flags.views import FlaggedViewMixin, FlaggedTemplateView
Quickstart
import os
# settings.py
INSTALLED_APPS = [
# ...
'flags',
# ...
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'context_processors': [
# ...
'django.template.context_processors.request',
# ...
],
},
},
]
FLAGS = {
'MY_NEW_FEATURE': [
{'condition': 'boolean', 'value': os.environ.get('ENABLE_MY_NEW_FEATURE', 'False').lower() == 'true'}
],
'BETA_ACCESS': [
{'condition': 'user', 'value': 'admin'},
{'condition': 'parameter', 'value': 'beta', 'required': False} # Can be overridden by URL param `?beta=true`
]
}
# views.py
from django.http import HttpResponse
from flags.state import flag_enabled
def my_feature_view(request):
if flag_enabled('MY_NEW_FEATURE', request=request):
return HttpResponse("Welcome to the new feature!")
return HttpResponse("Feature coming soon.")
# urls.py
from django.urls import path
from flags.urls import flagged_path
from . import views
urlpatterns = [
path('home/', views.my_feature_view, name='home'),
flagged_path('BETA_ACCESS', 'beta-page/', views.my_beta_view, name='beta_page')
]
# After adding to INSTALLED_APPS and settings, run migrations:
# python manage.py migrate