plain.flags
Local feature flags via database models.
Custom flags are written as subclasses of Flag
.
You define the flag's "key" and initial value,
and the results will be stored in the database for future reference.
# app/flags.py
from plain.flags import Flag
class FooEnabled(Flag):
def __init__(self, user):
self.user = user
def get_key(self):
return self.user
def get_value(self):
# Initially all users will have this feature disabled
# and we'll enable them manually in the admin
return False
Use flags in HTML templates:
{% if flags.FooEnabled(request.user) %}
<p>Foo is enabled for you!</p>
{% else %}
<p>Foo is disabled for you.</p>
{% endif %}
Or in Python:
import flags
print(flags.FooEnabled(user).value)
Installation
INSTALLED_PACKAGES = [
...
"plain.flags",
]
Create a flags.py
at the top of your app
(or point settings.FLAGS_MODULE
to a different location).
Advanced usage
Ultimately you can do whatever you want inside of get_key
and get_value
.
class OrganizationFeature(Flag):
url_param_name = ""
def __init__(self, request=None, organization=None):
# Both of these are optional, but will usually both be given
self.request = request
self.organization = organization
def get_key(self):
if (
self.url_param_name
and self.request
and self.url_param_name in self.request.GET
):
return None
if not self.organization:
# Don't save the flag result for PRs without an organization
return None
return self.organization
def get_value(self):
if self.url_param_name and self.request:
if self.request.GET.get(self.url_param_name) == "1":
return True
if self.request.GET.get(self.url_param_name) == "0":
return False
if not self.organization:
return False
# All organizations will start with False,
# and I'll override in the DB for the ones that should be True
return False
class AIEnabled(OrganizationFeature):
pass
1from plain.runtime import settings
2
3from . import exceptions
4from .flags import Flag
5
6
7def get_flags_module():
8 flags_module = settings.FLAGS_MODULE
9
10 try:
11 return __import__(flags_module)
12 except ImportError as e:
13 raise exceptions.FlagImportError(
14 f"Could not import {flags_module} module"
15 ) from e
16
17
18def get_flag_class(flag_name: str) -> Flag:
19 flags_module = get_flags_module()
20
21 try:
22 flag_class = getattr(flags_module, flag_name)
23 except AttributeError as e:
24 raise exceptions.FlagImportError(
25 f"Could not find {flag_name} in {flags_module} module"
26 ) from e
27
28 return flag_class