jazzband / django-waffle Goto Github PK
View Code? Open in Web Editor NEWA feature flipper for Django
Home Page: https://waffle.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
A feature flipper for Django
Home Page: https://waffle.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
It would be great to have mixins for working with CBVs.
If the waffle_flag table had a created timestamp column then we could tell how old a flag is. This would help to identify any flag code that can be deleted since they would no longer need to be disabled.
I'm envisioning using waffle more as an ephemeral way to roll out new code (for QA and for safety), not really as a way to turn features on and off.
Neither, WAFFLE_SWITCH_DEFAULT, WAFFLE_FLAG_DEFAULT, or WAFFLE_SAMPLE_DEFAULT have any effect on the waffle.js script.
We (SUMO) use waffle for A/B testing of behavior in the search view. One problem with this is that in order to derive CTR metrics for the A/B test so we can see whether it's helping/hindering, we need to thread the waffle flag name and value into all the search forms on the site and then also in the urls for search results.
This is a pain in the ass.
It's probably the case that we could alleviate some of the annoyingness in the SUMO code by centralizing a bunch of that code. However, James contends that waffle could/should make this easier regardless.
Start using get_user_model
and settings.AUTH_USER_MODEL
if they're there.
If you're using Memcache as your cache backend and have a space in your Flag, Switch or Sample name memcache throws and error on saving. MemcachedKeyCharacterError: Control characters not allowed
This is because with Memcache you can't have spaces in the key.
While waffle/compat/py
contains compatibility code for Django 1.5 and its custom user model 1, the south migrations are broken, as still contain references to the django default auth.user table 2.
I don't really know how to fix this, but it's not possible to install django-waffle on django 1.5 at the moment...
Typically, a package will have a VERSION variable in order to determine the version of the package from Python. I don't see this for waffle.
I want to set switch states from the JavaScript console to help debug my code.
This seems to be an issue in the migrations.
It looks as if auto_now
and auto_now_add
were being used previously, but a change was made to manually use datetime.now. Why is that?
This happens for me when running tests (creating the test DB).
Given:
myflag
is set up with testing=True
waffle.flag_is_active(request, 'myflag')
, and none of the templates it loads have {% flag myflag %}
dwft_myflag
cookie as False
, or does not have that cookie.Then (current behavior):
?dwft_myflag=1
dwft_myflag
cookie is never set to True
myflag
is treated as False
Desired behavior:
?dwft_myflag=1
dwft_myflag
cookie is set to True
myflag
is treated as True
This should be fixed by use default
and overriding the save
method instead, e.g. 775d4b0
If you call flag_is_active()
twice in a request, the first time a given user encounters this flag, Waffle will reroll the dice instead of using the value from the first roll.
This only happens iff you hit a dice roll (that is flag.percent > 0
and the user matched for no other reason). Inside the if flag.percent > 0:
block, we need to check request.waffles
for existing data.
For caches like Zeus, we probably need to do something like send a custom header and set Vary
on it, similar to X-Mobile. Unless we can make Zeus vary on specific cookies, which would be awesome.
WAFFLE_FLAG_DEFAULT = True
...in settings does not make this return True as expected:
waffle.flag_is_active('non-existant')
The same applies to template helpers and switches.
Just like Gargoyle:
http://gargoyle.readthedocs.org/en/latest/install/index.html#default-switch-states
Although I think Gargoyle only adds the switches when they're used. So it's autocreation (see the other Issue), but with the autocreation using stuff from settings if available.
GARGOYLE_SWITCH_DEFAULTS = {
'new_switch': {
'is_active': True,
'label': 'New Switch',
'description': 'When you want the newness',
},
'funky_switch': {
'is_active': False,
'label': 'Funky Switch',
'description': 'Controls the funkiness.',
},
}
When I used Gargoyle I always committed an addition to this setting along with whatever switch-controlled feature I was adding, to provide documentation and so noone would need to add the switch manually. I'd actually prefer all the switches to be added at startup though (nonexistant ones).
Currently I'm getting away with ./manage.py loaddata waffle.json each time I deploy, but that's not ideal.
I'll probably end up doing this myself as we'll want this in production soon. Comments welcome though about what I might best do and how I might do it. Perhaps this should be done with WoLpH (Rick van Hattem) and #44.
I'm ONLY using switches, currently, by the way.
It should be easier to include the waffle JS URLs with a waffle.urls
module you could just include()
.
Activate (or deactivate) a flag based on User-Agent. Need to support at least some way of forcing a flag value for search engine spiders, and it's potentially valuable for testing as well.
For some reason, the decorators do return HttpResponseNotFound()
instead of the normal raise Http404
, which returns blank pages instead of the nice 404 you've got set up. Fix incoming.
Hi,
django.core.context_processors.request is required by waffle, but there is no explicit requirement in the docs. Add this to avoid beginners disappointment , thus this context processor is not default one! : )
If you have more than a few waffles it's going to be tough to remember what they all are without some notes. Thanks!
When doing reporting of errors, it'd be nice to be able to have a method to get a hash of active flag/switches/samples which were on for the request. There's not really a great way to do that right now although the basics are all there in wafflejs.
Would you be open to a patch that pulled such a thing into a utility function that can be used by wafflejs as well as able to be used for error reporting purposes? If so, I can do the quick refactor and pull request.
Basically:
<div class="user-info {% if waffle.flag('foo') %}beta{% endif %}">{# This is really long #}
<div class="user-info {{ waffle.something('foo', 'beta') }}">{# Sugar, but kind of nice. #}
Would want 3 something
s, for Flags, Switches and Samples. Maybe it can just be a second, optional argument? That would be fancy!
I was curious about how the samples work. When you allow a certain percentage of users through the test I was wondering whether or not you record the users so that if the test would be run again with the sample active the same users would be subject to the test or it would once again be a random sampling of users.
Thanks.
The active flags, switches, and samples are transmitted to the client via the wafflejs
view, but the only way they are exposed as an API is via querying a given name for existence.
For the use case of automatically inject an entire list of active flags, etc. into a namespace for rendering a template using JavaScript, I propose adding some functions to window.waffle
that will return such lists:
active_flags()
active_switches()
active_samples()
waffle hints that there are other feature flippers (there are), so it might be nice to mention why you'd choose waffle (or not choose waffle) over the others
As you add more and more flags, wafflejs becomes increasingly heavyweight due to doing multiple db queries per flag and doing no batching at all.
I've currently got ~ 20 flags/switches and loading wafflejs requires 65 sql queries and 90 cache calls and over a second on my local app. Making it so that I replicate most of the logic from flag_is_active() and then using prefetch_related to get things into fewer queries gets to 10 queries without any cache calls and cuts the time to 0.3 secs. But that's not something that's really mainline-able in its current form. Will try to take a second pass at it unless you have an obvious idea but wanted to put it out there
My IDE is having a fit, although the code is working for me.
manage.py wafflescan should show all the switches, flags, or samples that are checked in the codebase.
Waffle flags are conceptually bound to a request, but it would be very useful to be able to flip switches globally, that are not bound to a request (one could do this by checking a flag's everyone
value but that's a hack).
This one may require some API change. Yay for version <1!
Even though it's in the git repo, there is no waffle.js in the pypi tarball.
That results in TemplateDoesNotExist at /waffle.js error being thrown when trying to access your waffle JS.
If a flag is enabled in the testing cookie, the query param is ignored. The query param should take precedence. Pull request coming in in a few.
Are there any caveats with using Waffle Switches to conditionally change settings in my project's settings.py?
For instance, is it required to restart the Django process for the changes to take effect?
And if a request is required, what is the best way to do this automatically?
Also on SO: http://stackoverflow.com/questions/19250316/using-waffle-to-toggle-settings-py
It would be valuable to turn on flags by internal IP or subnet. A comma-separated list of IP addresses with wild cards would be great.
Manually having to create these is kind of tedious and unneeded.
Automatically creating them if they don't exist (configurable with a setting of course) would be better :)
As a switch user,
I want non-existent switch values to be cached just like existing inactive switches,
So that my database isn't overloaded.
Given a switch_is_active('record_name') is called
When record_name does not exist
Then record_name is cached as inactive in the same way that an actual inactive record would be cached.
Tech Notes:
If you set WAFFLE_DEFAULT=True, that doesn't affect switches.
Looks to be a documentation bug. Pull request forthcoming.
The docs right now pretty well suck. They need to be reorganized. I've been working on this on the jsocol/docs branch, and it will include getting everything RTD-better.
Flags currently assume that the User model must have a 'Group' field. This was an OK assumption until Django 1.5 allowed for custom User models, which only requires these minimum requirements to be met:
If a custom User is being used with no group field, flag_is_active() throws an AttributeError exception at:
user_groups = user.groups.all()
since the User object has no such attribute.
I'm currently forking this repo in order to code a simple patch (Exception catching), but it might take a while to code a test case, so that it can be merged to the main repo. Anyone can feel free to beat me to the punch.
There is also the possibility of "fancy-ing it up" by implementing a way of removing the groups field from the flag model entirely (before db tables are created) IF a Django 1.5 group-less user is being used.
EDIT: There's also the assumption that the User object will have a is_superuser field (also throws an AttributeError exception in flag_is_active). However, a quick workaround is just to update the custom user model to take advantage of the @ property decorator. Still, it would be nice to have waffle check if these fields exist, and handle the cases that don't.
Documentation is off. The querystring parameter will be just the name_of_flag. No dwft prefix. Had to look it up in source code. :)
The querystring parameter will be dwft_<name_of_flag>. For example, if I have a flag named “ab_testing”, then I can override the setting with these urls:
http://example.com/?dwft_ab_testing=0 – Off
http://example.com/?dwft_ab_testing=1 – On
I currently have this in code I'm using to build test fixtures:
Switch.objects.create(name='avoid_accidental_drags', active=True)
If this switch already exists, I'm in trouble. I'd like a way of setting the state of a switch. And of having it autocreated (also #44) if necessary.
I can probably work on this as I'll want this in production soon. Comments please!
The 'enable_for_all' and 'disable_for_all' actions of the FlagAdmin should also iterator over the queryset and call .save() on every object like the enable/disable_switches actions of SwitchAdmin do since queryset.update() doesn't triggers the save signals and the cache not gets invalidated.
Once a user has a flag disabled, they're stuck with it off. That's fine for AB testing or situations where you don't expect a lot of repeat traffic, but if you want to use Waffle to roll out a feature incrementally it sucks.
"Dial-up" mode would, I think, essentially set session cookies when the flag is inactive, and semi-permanent cookies when the flag is active. That way, if you had the percentage at 5%, users would have a 5% chance to activate the flag every time they started a new session, but once it was on, it would stay on.
Need to think a little on how to make that interact with #1 (sticking a flag to a user) but it should be doable.
I've got an application which has a certain feature which is only useful when a big load of data is loaded into the database. I would like to do something like this:
$ ./manage.py switch_off my_feature
$ ./manage.py loaddata fixtures/my_big_pile_of_data.json
$ ./manage.py switch_on my_feature
This would obviously require the switch_on
and switch_off
commands to be implemented. Any chance this will appear soon?
If a user is authenticated, it doesn't make sense to flip back and forth depending on their device.
I guess a ForeignKey
through a WaffleUser
model that stored a boolean is the best way to do that.
Crystal would like to be able to set a flag via the query string and have it stay on for the whole session, but just the session, for user testing new features (as opposed to big, percentage-based switching). Shouldn't be too hard. (Already wrote it, just filing for records.)
Right now for running a test with a flag you have to do one of the following:
with patch.dict(jingo.env.globals['waffle'], {'flag': lambda x: True}):
# get a view using {% if waffle.flag(...) %}
or
with patch('waffle.flag_is_active') as flag:
flag.return_value = True
# get a view using if waffle.flag_is_active(...)
(and the same for switches)
These are both cumbersome. The first one also has a major problem in that the jingo.env is created lazily and the waffle key won't exist so you have to do import waffle.helpers
beforehand.
Am I missing an easier way?
Another minor problem is that the test code in no way references the name of the flag or switch so you can't find/replace it later on when removing the flag.
I don't have a solution in mind but I wanted to submit this so I don't forget to think about it. Maybe there needs to be something built into waffle specifically for testing. I know Andy McKay has taken some stabs at solving this too.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.