Skip to content

Commit

Permalink
Merge e36c18b into 9d7f50d
Browse files Browse the repository at this point in the history
  • Loading branch information
willbarton committed Jun 10, 2020
2 parents 9d7f50d + e36c18b commit 2e8fe65
Show file tree
Hide file tree
Showing 18 changed files with 72 additions and 299 deletions.
6 changes: 0 additions & 6 deletions .github/workflows/test.yml
Expand Up @@ -32,21 +32,15 @@ jobs:
strategy:
matrix:
toxenv:
- py36-dj111
- py36-dj22
- py36-dj30
- py38-dj111
- py38-dj22
- py38-dj30
include:
- toxenv: py36-dj111
python-version: 3.6
- toxenv: py36-dj22
python-version: 3.6
- toxenv: py36-dj30
python-version: 3.6
- toxenv: py38-dj111
python-version: 3.8
- toxenv: py38-dj22
python-version: 3.8
- toxenv: py38-dj30
Expand Down
51 changes: 0 additions & 51 deletions docs/api/urls.md
Expand Up @@ -2,8 +2,6 @@

Flagged URL patterns are an alternative to [flagging views with decorators](../../api/decorators).

## Django 2.0+

```python
from flags.urls import flagged_path, flagged_paths, flagged_re_path, flagged_re_paths
```
Expand Down Expand Up @@ -56,52 +54,3 @@ with flagged_paths('MY_FLAG') as path:

urlpatterns = urlpatterns + flagged_url_patterns
```

## Django 1.x

```python
from flags.urls import flagged_url, flagged_urls
```

### `flagged_url(flag_name, regex, view, kwargs=None, name=None, state=True, fallback=None)`

Make a URL depend on the state of a feature flag.

`flagged_url()` can be used in place of [Django's `url()`](https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url).

The `view` and the `fallback` can both be a set of `include()`ed patterns but any matching URL patterns in the includes must match *exactly* in terms of regular expression, keyword arguments, and name, otherwise a `404` may be unexpectedly raised.

If a `fallback` is not given the flagged url will raise a `404` if the flag state does not match the required `state`.

!!! note
When a fallback view is given it *must* take the same arguments as the flagged view.

```python
urlpatterns = [
flagged_url('MY_FLAG', r'a-url/', view_requiring_flag, state=True),
flagged_url('MY_FLAG_WITH_FALLBACK', r'^another-url$',
view_with_fallback, state=True, fallback=other_view)
flagged_url('MY_FLAGGED_INCLUDE', '^myapp/', include('myapp.urls'),
state=True, fallback=other_view)
flagged_url('MY_NEW_APP_FLAG', r'^mynewapp$', include('mynewapp.urls'),
state=True, fallback=include('myoldapp.urls'))
]
```

### `flagged_urls(flag_name, state=True, fallback=None)`

Flag multiple URLs in the same context with a context manager.

`flagged_urls()` returns a function that takes the same arguments as [Django's `url()`](https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url).

!!! note
When a fallback view is given it *must* take the same arguments as the flagged view.

```python
with flagged_urls('MY_FLAG') as furl:
flagged_url_patterns = [
furl(^'a-url/', view_requiring_flag),
]

urlpatterns = urlpatterns + flagged_url_patterns
```
28 changes: 0 additions & 28 deletions docs/api/views.md
Expand Up @@ -51,17 +51,6 @@ urlpatterns = [
]
```

#### Django 1.x

```python
from django.urls import path
from flags.urls import flagged_path

urlpatterns = [
url(r'^my-url/$', MyFlaggedView.as_view(flag_name='MY_FLAG'))
]
```

### `FlaggedTemplateView`

A combination of [`TemplateView`](https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#templateview) and [`FlaggedViewMixin`](#flaggedviewmixin).
Expand Down Expand Up @@ -92,20 +81,3 @@ urlpatterns = [
),
]
```

#### Django 1.x

```
from django.urls import url
from flags.views import FlaggedTemplateView
urlpatterns = [
url(
r'^my_url/$',
FlaggedTemplateView.as_view(
template_name='mytemplate.html',
flag_name='MY_FLAG'
)
),
]
```
15 changes: 1 addition & 14 deletions docs/index.md
Expand Up @@ -4,7 +4,7 @@ Feature flags allow you to toggle functionality in both Django code and the Djan

## Dependencies

- Django 1.11+ (including Django 2)
- Django 2.2, 3.0
- Python 3.6+

## Installation
Expand Down Expand Up @@ -85,8 +85,6 @@ Then use the flag in a Django template (`mytemplate.html`):

Configure a URL for that template (`urls.py`):

Django 2.0:

```python
from django.urls import path
from django.views.generic import TemplateView
Expand All @@ -96,17 +94,6 @@ urlpatterns = [
]
```

Django 1.x:

```python
from django.conf.urls import url
from django.views.generic import TemplateView

urlpatterns = [
url(r'^mypage/$', TemplateView.as_view(template_name='mytemplate.html')),
]
```

Then in the Django admin add conditions for the flag in "Django Flags", "Flag states":

![Creating conditions in the Django admin](images/screenshot_create.png)
Expand Down
11 changes: 10 additions & 1 deletion docs/releasenotes.md
@@ -1,13 +1,22 @@
# Release Notes

## Unreleased
## 5.0.0

### What's new?

- Added Django 3.0 support
- Added validator support to ensure that the values that flag conditions test against are valid.

### Deprecations

- Deprecated the optional `flags.middleware.FlagConditionsMiddleware` in favor of always lazily caching flags on the request object.

### Removals

- Django Flags 4.1 deprecated support for using a single dictionary to hold key/values of conditions for a settings-based feature flag, and this has been removed. Use [a list of dictionaries or tuples instead](/settings/#flags).
- Removed support for Django 1.11.


## 4.2.4

### What's new?
Expand Down
16 changes: 3 additions & 13 deletions docs/usage.md
Expand Up @@ -33,7 +33,7 @@ if flag_enabled('FLAG_WITH_ANY_CONDITIONS', request=a_request):
print("My feature flag is enabled")
```

Django templates:
In Django templates:

```django
{% load feature_flags %}
Expand All @@ -45,7 +45,7 @@ Django templates:
{% endif %}
```

Jinja2 templates (after [adding `flag_enabled` to the Jinja2 environment](../api/jinja2/)):
In Jinja2 templates (after [adding `flag_enabled` to the Jinja2 environment](../api/jinja2/)):

```jinja
{% if flag_enabled('FLAG_WITH_ANY_CONDITIONS', request) %}
Expand All @@ -55,7 +55,7 @@ Jinja2 templates (after [adding `flag_enabled` to the Jinja2 environment](../api
{% endif %}
```

Django 2.0 `urls.py`:
In `urls.py`:

```python
from flags.urls import flagged_path
Expand All @@ -65,14 +65,4 @@ urlpatterns = [
]
```

And Django 1.x `urls.py`:

```python
from flags.urls import flagged_url

urlpatterns = [
flagged_url('FLAG_WITH_REQUIRED_CONDITIONS', r'^a-url$', view_requiring_flag, state=True),
]
```

See the [API reference](/api/state) for more details and examples.
6 changes: 1 addition & 5 deletions flags/conditions/conditions.py
@@ -1,7 +1,6 @@
import re
from distutils.util import strtobool

import django
from django.contrib.auth import get_user_model
from django.utils import dateparse, timezone

Expand Down Expand Up @@ -46,10 +45,7 @@ def anonymous_condition(boolean_value, request=None, **kwargs):
"request is required for condition 'anonymous'"
)

if django.VERSION[0] >= 2: # pragma: no cover
is_anonymous = bool(request.user.is_anonymous)
else: # pragma: no cover
is_anonymous = bool(request.user.is_anonymous())
is_anonymous = bool(request.user.is_anonymous)

try:
return strtobool(boolean_value.strip().lower()) == is_anonymous
Expand Down
11 changes: 0 additions & 11 deletions flags/sources.py
@@ -1,5 +1,4 @@
import logging
import warnings

from django.apps import apps
from django.conf import settings
Expand Down Expand Up @@ -89,16 +88,6 @@ def get_flags(self):
settings_flags = getattr(settings, "FLAGS", {}).items()
flags = {}
for flag, conditions in settings_flags:
# Flag conditions in settings used to be dicts, which are now
# deprecated.
if isinstance(conditions, dict):
warnings.warn(
"dict feature flag definitions are deprecated and will be "
"removed in a future version of Django-Flags. "
"Please use a list of dicts or tuples instead.",
FutureWarning,
)
conditions = conditions.items()

# Flag conditions should be a list of either 3-tuples of
# dictionaries in the form (condition, value, required) or
Expand Down
10 changes: 2 additions & 8 deletions flags/templatetags/feature_flags.py
@@ -1,4 +1,3 @@
import django
from django import template

from flags.state import (
Expand All @@ -9,25 +8,20 @@

register = template.Library()

if django.VERSION >= (1, 9): # pragma: no cover
simple_tag = register.simple_tag
else: # pragma: no cover
simple_tag = register.assignment_tag


# Creates template tags flag_enabled and flag_disabled that call
# base_flag_enabled and base_flag_disabled, passing in any arguments, including
# the request (which could be passed explicitly, or pulled from the context).


@simple_tag(takes_context=True)
@register.simple_tag(takes_context=True)
def flag_enabled(context, flag_name, request=None, **kwargs):
if request is None:
request = context.get("request")
return base_flag_enabled(flag_name, request=request, **kwargs)


@simple_tag(takes_context=True)
@register.simple_tag(takes_context=True)
def flag_disabled(context, flag_name, request=None, **kwargs):
if request is None:
request = context.get("request")
Expand Down
20 changes: 6 additions & 14 deletions flags/tests/settings.py
@@ -1,7 +1,5 @@
import os

import django

from flags.conditions import register


Expand Down Expand Up @@ -32,18 +30,12 @@

INSTALLED_APPS += ("flags", "flags.tests.testapp")

if django.VERSION >= (1, 10): # pragma: no cover
MIDDLEWARE = (
"debug_toolbar.middleware.DebugToolbarMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
)
else: # pragma: no cover
MIDDLEWARE_CLASSES = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
)
MIDDLEWARE = (
"debug_toolbar.middleware.DebugToolbarMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
)

TEMPLATES = [
{
Expand Down
7 changes: 1 addition & 6 deletions flags/tests/test_admin.py
@@ -1,12 +1,7 @@
from django.contrib import admin
from django.contrib.auth.models import User
from django.test import Client, TestCase, override_settings


try:
from django.urls import re_path
except ImportError: # pragma: no cover
from django.conf.urls import url as re_path
from django.urls import re_path


urlpatterns = [
Expand Down
7 changes: 1 addition & 6 deletions flags/tests/test_decorators.py
@@ -1,17 +1,12 @@
import warnings
from unittest.mock import Mock

from django.http import Http404, HttpRequest, HttpResponse
from django.test import TestCase

from flags.decorators import flag_check, flag_required


try:
from unittest.mock import Mock
except ImportError: # pragma: no cover
from mock import Mock


class FlagCheckTestCase(TestCase):
def setUp(self):
self.request = HttpRequest()
Expand Down

0 comments on commit 2e8fe65

Please sign in to comment.