Affect is a flagging engine which applies a flag value to requests based on defined criteria. While largely inspired by django-waffle, Affect focuses less on feature and rollout flags and more on changing user experience on a more detailed and longer term basis.
Affect has been tested and working with Django 1.8 requires django-extensions.
For support for Django 1.4, 1.5, 1.6 use version 1.1.0
Install from github with pip:
pip install -e git@github.com:ConsumerAffairs/django-affect.git#django-affect
Or from PyPI:
pip install django-affect
Add to installed apps and middleware
INSTALLED_APPS = (
...
'affected',
)
...
MIDDLEWARE_CLASSES = (
...
'affect.middleware.AffectMiddleware',
)
Affect expects that the Django Authentication middleware is in use as well.
Setup the database models using ./manage.py migrate affect
if you are using South, or ./manage.py syncdb
if you are not.
###Creating Criteria and Flags###
Affect core functionality is broken into two models Criteria and Flags. Criteria are the decision making settings which are used to decide which Flags should be made "active". You can use the Django Admin to create new Criteria and Flags.
####Criteria fields####
name
- used for storing and identifying this criteria. It is a slug field and is restricted to letters, numbers, underscores(_) and hyphens(-)
flags
- flags objects to activate when this criteria is met
persistent
- mark this if you would like all future requests to respect this criteria as the decision made for this request. Most useful when using entry_url
, referrer
and query_args
. When testing
args or percent
-based assignment, persistent is implied.
max_cookie_age
- the maximum age in seconds to store persistent or testing cookies. Default is 30 days, 0 or blank creates a session cookie. Age is updated on each request.
everyone
- is active (yes) or inactive (no) for everyone, overriding all other options except persistent. Usually should be Unknown.
testing
- allows you to use querystring args to override criteria status. Add ?dact_{{criteria_name}}=
with 1
to enable and 0
to disable. This is implied persistent and sets a cookie.
percent
- enables criteria for a percentage of users. Implies persistent and sets a cookie.
superusers
- enables for all Superusers.
staff
- enables for all staff users.
authenticated
- enables for all authenticated users.
device_type
- attempt to detect and enable for users with a class of devices, mobile/table, desktop, or simple device/dumb phone. The practice of device detection is generally a bad idea. Use only for cases where end-users will not see results, such as server side logging. Use CSS and JS to detect features client-side for anything the user sees, they're up to the task.
entry_url
- comma-separarted list of urls to enable criteria when user enters on them. Any domain other than that of the current request and any listed in the AFFECTED_NONENTRY_DOMAINS
setting will be considered an entry.
referrer
- comma-separated list of domains to enable criteria when referring page matchs.
query_args
- a dictionary of key-value pairs to match in GET querystring. {"foo": "bar"}
matches ?foo=bar
, {'foo': '*'}
matches ?foo=<any value>
, and {'foo': ['bar', 'baz']}
matches ?foo=bar
or ?foo=baz
. Key-values in querystring not defined here are ignored by Affect.
groups
- user groups that enable criteria when user is a member of one or more of those groups.
users
- sepecific users to enable criteria for.
notes
- they're a good idea. Helps you remember what you intended.
####Flag fields####
name
- identifying slug for this flag. Must be unique, using letters, numbers, underscores or hyphens only.
active
- if not active, flag will not be enabled for anyone, regardless of criteria matches.
conflicts
- other flags which conflict with this one. In the case that two flags affect similar features or functionality, mark them here and set priority
accordingly.
priority
- in event of conflicts
, only the flag with the highest priority is enabled and all other conflicts ignored.
###"Looking" for Flags##
In Affect, Flags are the primary indicator of whether action should be taken by your code. Passing the flag_is_affected
function the request object and flag name will tell you if the flag is enabled for this request.
Due to compatibility issues with later versions of Django, the convenience import path for affect.flag_is_affected
no longer exists and you must import from affect.utils.flag_is_affected
.
from affect.utils import flag_is_affected
def sample_view(request):
if flag_is_affected(request, 'template_rev_b'):
template = 'app/new_template.html'
else:
template = 'app/old_template.html'
####Use in Templates####
If you are using RequestContext when passing context to your template, you can also check for the flag from within the template. This work for both Django and Jinja templates.
{% if 'rev_b' in request.affected_flags %}
<div class="new-style">
{% else %}
<div class="old-style">
{% endif %}
###Settings###
AFFECTED_NONENETRY_DOMAINS
- A list of domains to exclude when deciding if a user if entering your site. ['example.com', 'www.example.net']
will exclude example.com and www.example.net from entry detection, (this would not exclude www.example.com or example.net)
There are a few ways coookies are set to insure persistence. These cookies affect how the cookies are stored
AFFECTED_SECURE_COOKIE
- Encrypt affect cookies (default: False
)
AFFECTED_COOKIE
- String formatting to apply to criteria names for persistent flags, such as dac_tester
for criteria called "tester". (default: 'dac_%s'
)
AFFECTED_TESTING_COOKIE
- String formatting to apply to criteria names when using testing functionality (default: 'dact_%s'
)
Install requirements
pip install fabric
pip install -r requirements.txt
Setup db
fab syncdb
fab migrate
Start server
fab serve
Using the Python shell
fab shell
Running tests
fab test
Creating South schema migrations
fab schema
fab migrate
All pull requests should pass pep8 and pyflakes validation and have 100% test coverage and be developed in a separate feature branch.