Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Django 2.1 decorators #76

Merged
merged 3 commits into from Sep 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions .travis.yml
@@ -1,4 +1,5 @@
language: python
dist: xenial
python:
- "2.7"
- "3.4"
Expand All @@ -10,10 +11,20 @@ env:
- DJANGO_VERSION="django>=1.10,<1.11"
- DJANGO_VERSION="django>=1.11,<1.12"
- DJANGO_VERSION="django>=2.0,<2.1"
- DJANGO_VERSION="django>=2.1,<2.2"
- DJANGO_VERSION="django>=2.2,<2.3"
matrix:
exclude:
- python: "2.7"
env: DJANGO_VERSION="django>=2.0,<2.1"
- python: "2.7"
env: DJANGO_VERSION="django>=2.1,<2.2"
- python: "2.7"
env: DJANGO_VERSION="django>=2.2,<2.3"
- python: "3.4"
env: DJANGO_VERSION="django>=2.1,<2.2"
- python: "3.4"
env: DJANGO_VERSION="django>=2.2,<2.3"
install:
- pip install -r requirements.txt
- pip install $DJANGO_VERSION
Expand Down
9 changes: 9 additions & 0 deletions stronghold/decorators.py
Expand Up @@ -8,8 +8,17 @@ def public(function):
Sets an attribute in the fuction STRONGHOLD_IS_PUBLIC to True
"""
orig_func = function
outer_partial_wrapper = None
while isinstance(orig_func, partial):
outer_partial_wrapper = orig_func
orig_func = orig_func.func
# For Django >= 2.1.x:
# If `function` is a bound method, django will wrap it in a partial
# to allow setting attributes on a bound method.
# Bound methods have the attr "__self__". If this is the case,
# we reapply the partial wrapper before setting the attribute.
if hasattr(orig_func, "__self__") and outer_partial_wrapper != None:
orig_func = outer_partial_wrapper
set_view_func_public(orig_func)

return function
9 changes: 9 additions & 0 deletions stronghold/tests/testdecorators.py
Expand Up @@ -7,6 +7,7 @@
from django.utils import unittest
else:
import unittest
from django.utils.decorators import method_decorator


class StrongholdDecoratorTests(unittest.TestCase):
Expand Down Expand Up @@ -37,3 +38,11 @@ def function():
decorators.public(partial)

self.assertTrue(function.STRONGHOLD_IS_PUBLIC)

def test_public_decorator_works_with_method_decorator(self):
class TestClass:
@method_decorator(decorators.public)
def function(self):
pass

self.assertTrue(TestClass.function.STRONGHOLD_IS_PUBLIC)
2 changes: 2 additions & 0 deletions test_project/test_project/urls.py
Expand Up @@ -5,4 +5,6 @@
urlpatterns = [
url(r'^protected/$', views.ProtectedView.as_view(), name="protected_view"),
url(r'^public/$', views.PublicView.as_view(), name="public_view"),
url(r'^public2/$', views.PublicView2.as_view(), name="public_view2"),
url(r'^public3/$', views.public_view3, name="public_view3")
]
11 changes: 11 additions & 0 deletions test_project/test_project/views.py
Expand Up @@ -3,6 +3,7 @@
from django.utils.decorators import method_decorator

from stronghold.decorators import public
from stronghold.views import StrongholdPublicMixin


class ProtectedView(View):
Expand All @@ -19,3 +20,13 @@ def dispatch(self, *args, **kwargs):

def get(self, request, *args, **kwargs):
return HttpResponse("PublicView")

class PublicView2(StrongholdPublicMixin, View):
""" A view we want to be public, using the StrongholdPublicMixin"""
def get(self, request, *args, **kwargs):
return HttpResponse("PublicView")

@public
def public_view3(request):
""" A function view we want to be public"""
return HttpResponse("PublicView")