|
| 1 | +# Django Waffle — Feature flags |
| 2 | + |
| 3 | +This document describes how **feature flags** work in the Boost website project (django-waffle), how to assign and manage them, and how the **v3** flag is set up. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## How feature flags work in this project |
| 8 | + |
| 9 | +- **Library:** [django-waffle](https://github.com/jazzband/django-waffle) (v5.0.0). Flags are stored in the database and evaluated per request (and optionally per user, group, or session). |
| 10 | +- **Middleware:** `waffle.middleware.WaffleMiddleware` runs on each request so that flag state is available in templates and views. |
| 11 | +- **Settings** (`config/settings.py`): |
| 12 | + - **`WAFFLE_CREATE_MISSING_FLAGS = True`** — The first time a flag name is used in code (e.g. `{% flag "v3" %}` or `flag_is_active(request, "v3")`), it is auto-created in the database with default **off**. |
| 13 | + - **`WAFFLE_FLAG_DEFAULT = False`** — Newly created flags have “Everyone” = No; they only become active when explicitly enabled for users, groups, percentage, etc. |
| 14 | +- **Evaluation:** For each request, waffle checks the flag’s rules (everyone, superusers, staff, authenticated, groups, users, percentage, testing cookie). If any rule matches, the flag is **active** for that request; otherwise it is **inactive**. Anonymous users only get a flag if “Everyone” is Yes or a percentage/testing rule applies; group-based activation applies only to authenticated users in the chosen groups. |
| 15 | + |
| 16 | +So in short: **flags are off by default**, and you turn them on by assigning **groups**, **users**, or other options in the Django admin. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## How to assign feature flags |
| 21 | + |
| 22 | +Feature flags are managed in the **Django Admin** under **Waffle**. |
| 23 | + |
| 24 | +### 1. Create or open a flag |
| 25 | + |
| 26 | +- Go to **Django Admin** → **Waffle** → **Flags** (`/admin/waffle/flag/`). |
| 27 | +- Either: |
| 28 | + - **Create** a new flag (Name = e.g. `v3`), or |
| 29 | + - **Open** an existing flag (e.g. `v3`). |
| 30 | + |
| 31 | +With `WAFFLE_CREATE_MISSING_FLAGS = True`, the flag may already exist because it was used in code; you only need to edit it. |
| 32 | + |
| 33 | +### 2. Choose who gets the flag |
| 34 | + |
| 35 | +On the flag’s edit page you can enable the flag for: |
| 36 | + |
| 37 | +- **Everyone** — Yes/No/Unknown. “Yes” turns the flag on for all requests (use with care). |
| 38 | +- **Superusers** — If checked, the flag is always on for superusers. |
| 39 | +- **Staff** — If checked, the flag is on for staff users. |
| 40 | +- **Authenticated** — If checked, the flag is on for any logged-in user. |
| 41 | +- **Groups (Chosen groups)** — The flag is on only for users who belong to at least one of the selected groups. This is the usual way to target testers (e.g. `v3_testers`). |
| 42 | +- **Users (Chosen users)** — The flag is on only for the selected users. |
| 43 | +- **Percentage** — Roll out to a percentage of users (0.0–99.9). |
| 44 | +- **Testing** — When enabled, the flag can be toggled via a query/cookie for testing (see waffle docs). |
| 45 | + |
| 46 | +For a **group-based** flag (e.g. v3): |
| 47 | + |
| 48 | +1. In **Chosen groups**, move the desired group (e.g. **v3_testers**) from “Available groups” to “Chosen groups”. |
| 49 | +2. Leave **Everyone** as “No” (or “Unknown”) so only the chosen group sees the flag. |
| 50 | +3. Click **Save**. |
| 51 | + |
| 52 | +### 3. Put users into the group |
| 53 | + |
| 54 | +Group-based flags only apply to **authenticated** users who are **members** of one of the chosen groups. |
| 55 | + |
| 56 | +- Go to **Users** (or **Auth** → **Users** / **Users** → **Users**, depending on your project). |
| 57 | +- Open the **user** that should see the flag. |
| 58 | +- In **Groups** (or “User groups”), add the group (e.g. **v3_testers**). |
| 59 | +- Save. |
| 60 | + |
| 61 | +After that, when that user is logged in, the flag is active for their requests. Log out and back in (or use a fresh session) if you don’t see the change. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## The “v3” flag and banner |
| 66 | + |
| 67 | +The **v3** flag is used to show a banner to users who are part of the v3 rollout. |
| 68 | + |
| 69 | +### What was added in the repo |
| 70 | + |
| 71 | +- **django-waffle** in `requirements.txt` (v5.0.0). |
| 72 | +- **Config** in `config/settings.py`: `waffle` in `INSTALLED_APPS`, `waffle.middleware.WaffleMiddleware` in `MIDDLEWARE`, `WAFFLE_CREATE_MISSING_FLAGS = True`, `WAFFLE_FLAG_DEFAULT = False`. |
| 73 | +- **Banner** in `templates/base.html`: `{% load waffle_tags %}` and a block `{% flag "v3" %} ... {% endflag %}` that shows a grey “v3 flag enabled” bar at the top of the page when the flag is active for the requesting user. |
| 74 | +- **Data migration** `users/migrations/0021_add_v3_testers_group.py` creates the Django auth group **v3_testers**, which can be assigned to the v3 flag. |
| 75 | + |
| 76 | +### How to enable the v3 banner for yourself |
| 77 | + |
| 78 | +1. **Apply migrations** (so the `v3_testers` group exists): |
| 79 | + ```bash |
| 80 | + just migrate |
| 81 | + # or: docker compose run --rm web python manage.py migrate |
| 82 | + ``` |
| 83 | +2. **Admin** → **Waffle** → **Flags** → open (or create) the **v3** flag. |
| 84 | +3. In **Chosen groups**, add **v3_testers** and save. |
| 85 | +4. **Admin** → **Users** → open **your user** → add **v3_testers** to Groups → save. |
| 86 | +5. Log in on the site and reload; the **“v3 flag enabled”** banner should appear at the top. |
| 87 | + |
| 88 | +### If the “v3 flag enabled” banner does not appear |
| 89 | + |
| 90 | +- You must be **logged in**; group-based flags do not apply to anonymous users. |
| 91 | +- Your **user** must be in the **v3_testers** group (Users → your user → Groups). |
| 92 | +- **Log out and log back in** (or use a new incognito session) so the session reflects the group. |
| 93 | +- Confirm the **v3** flag is saved with **v3_testers** in Chosen groups. |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## Where it is in the codebase |
| 98 | + |
| 99 | +| What | Where | |
| 100 | +|------|--------| |
| 101 | +| Conditional banner | `templates/base.html`: `{% load waffle_tags %}` and `{% flag "v3" %} ... {% endflag %}` | |
| 102 | +| Waffle config | `config/settings.py`: `INSTALLED_APPS`, `MIDDLEWARE`, `WAFFLE_*` | |
| 103 | +| v3_testers group | `users/migrations/0021_add_v3_testers_group.py` | |
| 104 | +| Package | `requirements.txt`: `django-waffle==5.0.0` | |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## Using flags in Python (views) |
| 109 | + |
| 110 | +```python |
| 111 | +from waffle import flag_is_active |
| 112 | + |
| 113 | +def my_view(request): |
| 114 | + if flag_is_active(request, "v3"): |
| 115 | + # Flag is active for this request (e.g. user in v3_testers) |
| 116 | + ... |
| 117 | + else: |
| 118 | + ... |
| 119 | +``` |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +## Using flags in templates |
| 124 | + |
| 125 | +```django |
| 126 | +{% load waffle_tags %} |
| 127 | +
|
| 128 | +{% flag "v3" %} |
| 129 | + <div>Content only shown when the v3 flag is active for this user.</div> |
| 130 | +{% endflag %} |
| 131 | +``` |
| 132 | + |
| 133 | +You can use the same pattern for any flag name (e.g. `{% flag "my_feature" %}`) and assign it via groups or users in the admin as described above. |
0 commit comments