/Docs

Django integration

This guide targets Django 4.2+ running on Python 3.11 or later. By the end your Django application will initialise the A vs B SDK once on startup, expose a per-request helper on every HttpRequest object via middleware, and cleanly shut down when the process exits.

1

Install

terminal
bash
1pip install avsb-python
2

Obtain your SDK key

Open your A vs B project, go to Settings → Environments, and copy the Server SDK key for the target environment. Add it to your environment — never hardcode it in settings.py.

.env
bash
1AVSB_SDK_KEY=sdk_production_...
3

Configure Django settings

Add the A vs B middleware to MIDDLEWARE and set your SDK key. The middleware must appear early enough that downstream middleware and views can access request.avsb.

settings.py
python
1import os
2
3MIDDLEWARE = [
4 'django.middleware.security.SecurityMiddleware',
5 'avsb.contrib.wsgi.AvsbMiddleware', # <-- add this
6 'django.contrib.sessions.middleware.SessionMiddleware',
7 # ... rest of your middleware
8]
9
10AVSB_SDK_KEY = os.environ['AVSB_SDK_KEY']
4

Create an initialiser (AppConfig)

Initialise the SDK singleton once when Django starts using an AppConfig.ready() hook. This avoids re-connecting on every request and ensures the datafile is available before the first request is served.

myapp/apps.py
python
1from django.apps import AppConfig
2
3
4class MyAppConfig(AppConfig):
5 default_auto_field = 'django.db.models.BigAutoField'
6 name = 'myapp'
7
8 def ready(self) -> None:
9 from django.conf import settings
10 from avsb import AvsbServer
11
12 # Store on the config so other modules can import it
13 self.__class__.avsb_server = AvsbServer(sdk_key=settings.AVSB_SDK_KEY)
14 self.__class__.avsb_server.on_ready_sync() # blocks briefly on first boot
on_ready_sync vs await on_ready()
on_ready_sync() is a convenience wrapper for the synchronous Django context. If you run Django with an ASGI server (e.g. Daphne or Uvicorn) and want fully async startup, use await server.on_ready() inside an async ready() or an ASGI lifespan handler instead.
5

Read a flag in a view

The middleware attaches a scoped helper to request.avsbusing the context it builds from the request (see the middleware's context_from option). The helper exposes typed flag methods.

myapp/views.py
python
1from django.http import JsonResponse
2from django.views import View
3
4
5class CheckoutView(View):
6 def get(self, request):
7 # request.avsb is pre-scoped to the current user context
8 show_new = request.avsb.get_bool_flag('checkout-v2', False)
9 theme = request.avsb.get_string_flag('ui-theme', 'default')
10 return JsonResponse({'showNewCheckout': show_new, 'theme': theme})
6

Configure context_from (optional)

By default the middleware builds an anonymous context. Pass a context_from callable in AVSB_MIDDLEWARE_OPTIONS to build a richer targeting context from the request.

settings.py
python
1AVSB_MIDDLEWARE_OPTIONS = {
2 'context_from': lambda request: {
3 'kind': 'user',
4 'key': str(request.user.id) if request.user.is_authenticated else 'anon',
5 'plan': getattr(request.user, 'plan', 'free'),
6 }
7}
7

Track an event

myapp/views.py
python
1request.avsb.track('purchase', value=149.99, properties={'currency': 'usd'})
8

Identify a user

If the user logs in during the request lifecycle and you need to re-evaluate flags against the authenticated identity, build a new scoped client directly from the server singleton:

myapp/views.py
python
1from myapp.apps import MyAppConfig
2
3server = MyAppConfig.avsb_server
4scoped = server.for_user({'kind': 'user', 'key': str(user.id), 'plan': user.plan})
5show_new = scoped.get_bool_flag('checkout-v2', False)

Graceful shutdown

Register a Django AppConfig shutdown signal or a plain atexit hook to flush and close the SDK before the process exits:

myapp/apps.py
python
1import atexit
2
3class MyAppConfig(AppConfig):
4 # ...
5 def ready(self) -> None:
6 # ... init as above ...
7 atexit.register(self.__class__.avsb_server.close_sync)

Testing

tests/test_flags.py
python
1import pytest
2from avsb.test_utils import MockAvsbServer, TestData
3
4@pytest.fixture
5def mock_server():
6 td = (
7 TestData.flag('checkout-v2')
8 .boolean_flag()
9 .variation_for_user('u_1', True)
10 .fallthrough_variation(False)
11 )
12 return MockAvsbServer(flags=[td.build()])
13
14def test_returns_true_for_u1(mock_server):
15 scoped = mock_server.for_user({'kind': 'user', 'key': 'u_1'})
16 assert scoped.get_bool_flag('checkout-v2', False) is True
17
18def test_returns_default_false(mock_server):
19 scoped = mock_server.for_user({'kind': 'user', 'key': 'other'})
20 assert scoped.get_bool_flag('checkout-v2', False) is False

What's next