# Decorating URL includes, from functools import wraps from django.conf import settings from django.contrib.auth.decorators import user_passes_test from django.core.exceptions import PermissionDenied from django.core.urlresolvers import RegexURLPattern, RegexURLResolver from django.views.debug import technical_404_response from django.http import Http404 class DecoratedURLPattern(RegexURLPattern): def resolve(self, *args, **kwargs): result = super(DecoratedURLPattern, self).resolve(*args, **kwargs) if result: result.func = self._decorate_with(result.func) return result class DecoratedRegexURLResolver(RegexURLResolver): def resolve(self, *args, **kwargs): result = super(DecoratedRegexURLResolver, self).resolve(*args, **kwargs) if result: result.func = self._decorate_with(result.func) return result def decorated_includes(func, includes, *args, **kwargs): urlconf_module, app_name, namespace = includes for item in urlconf_module: if isinstance(item, RegexURLPattern): item.__class__ = DecoratedURLPattern item._decorate_with = func elif isinstance(item, RegexURLResolver): item.__class__ = DecoratedRegexURLResolver item._decorate_with = func return urlconf_module, app_name, namespace # below, a set of decorators to generate urls.py like this: # urlpatterns = required( # app_enabled('applabel'), # [... urls for applabel ...] # ) def unless(test, message): '''Decorator returning a 404 status code if some condition is not met''' def decorator(func): @wraps(func) def f(request, *args, **kwargs): if not test(): return technical_404_response(request, Http404(message)) return func(request, *args, **kwargs) return f return decorator def app_enabled(app_label): '''for enabling a view based on PASSERELLE_APP__ENABLED flag''' def test(): return getattr(settings, 'PASSERELLE_APP_%s_ENABLED' % app_label.upper(), True) return unless(test, 'please enable %s' % app_label) def setting_enabled(name): '''for enabling a view based on a setting''' def test(): return getattr(settings, name, False) return unless(test, 'please enable %s' % name) # code bellow is borrowed from https://djangosnippets.org/snippets/2607/ # or https://gist.github.com/sjzabel/1378003 def required(wrapping_functions,patterns_rslt): ''' Used to require 1..n decorators in any view returned by a url tree Usage: urlpatterns = required(func,[...urls...]) urlpatterns = required((func,func,func),[...urls...]) Note: Use functools.partial to pass keyword params to the required decorators. If you need to pass args you will have to write a wrapper function. Example: from functools import partial urlpatterns = required( partial(login_required,login_url='/accounts/login/'), [...urls...] ) ''' if not hasattr(wrapping_functions, '__iter__'): wrapping_functions = (wrapping_functions, ) return [ _wrap_instance__resolve(wrapping_functions, instance) for instance in patterns_rslt ] def _wrap_instance__resolve(wrapping_functions, instance): def _wrap_func_in_returned_resolver_match(*args, **kwargs): rslt = resolve(*args, **kwargs) if not hasattr(rslt, 'func'): return rslt f = getattr(rslt, 'func') for _f in reversed(wrapping_functions): # @decorate the function from inner to outter f = _f(f) setattr(rslt, 'func', f) return rslt if not hasattr(instance, 'resolve'): return instance resolve = getattr(instance, 'resolve') setattr(instance, 'resolve', _wrap_func_in_returned_resolver_match) return instance def manager_required(function=None, login_url=None): def check_manager(user): if user and user.is_staff: return True if user and not user.is_anonymous: raise PermissionDenied() # As the last resort, show the login form return False actual_decorator = user_passes_test(check_manager, login_url=login_url) if function: return actual_decorator(function) return actual_decorator