如何在django基于类的视图上使用permission_required装饰器
了解新的CBV如何工作,我有点麻烦。 我的问题是,我需要在所有的意见,并在其中一些特定的权限login。 在基于函数的视图中,我使用@permission_required()和视图中的login_required属性来做到这一点,但我不知道如何在新视图上执行此操作。 django文档中有解释这个的一些部分吗? 我没有find任何东西。 我的代码有什么问题?
我尝试使用@method_decorator,但它回复“ /空/ prueba / _wrapped_view()至less需要1个参数(0给出) ” TypeError “
这里是代码(GPL):
from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required, permission_required class ViewSpaceIndex(DetailView): """ Show the index page of a space. Get various extra contexts to get the information for that space. The get_object method searches in the user 'spaces' field if the current space is allowed, if not, he is redirected to a 'nor allowed' page. """ context_object_name = 'get_place' template_name = 'spaces/space_index.html' @method_decorator(login_required) def get_object(self): space_name = self.kwargs['space_name'] for i in self.request.user.profile.spaces.all(): if i.url == space_name: return get_object_or_404(Space, url = space_name) self.template_name = 'not_allowed.html' return get_object_or_404(Space, url = space_name) # Get extra context data def get_context_data(self, **kwargs): context = super(ViewSpaceIndex, self).get_context_data(**kwargs) place = get_object_or_404(Space, url=self.kwargs['space_name']) context['entities'] = Entity.objects.filter(space=place.id) context['documents'] = Document.objects.filter(space=place.id) context['proposals'] = Proposal.objects.filter(space=place.id).order_by('-pub_date') context['publication'] = Post.objects.filter(post_space=place.id).order_by('-post_pubdate') return context
在CBV文档中列出了一些策略:
-
在你的
urls.py
路由中添加装饰器 ,例如login_required(ViewSpaceIndex.as_view(..))
-
用一个
method_decorator
装饰你的CBV的dispatch
方法,例如,from django.utils.decorators import method_decorator @method_decorator(login_required, name='dispatch') class ViewSpaceIndex(TemplateView): template_name = 'secret.html'
在Django 1.9之前,你不能在类上使用
method_decorator
,所以你必须重载dispatch
方法:class ViewSpaceIndex(TemplateView): @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
-
使用像Django 1.9+中提供的django.contrib.auth.mixins.LoginRequiredMixin这样的访问混合,并在其他答案中概述。
你得到TypeError
的原因在文档中解释:
注意:method_decorator将* args和** kwargs作为parameter passing给类上的装饰方法。 如果你的方法不接受一组兼容的参数,将引发一个TypeErrorexception。
这是我的方法,我创build一个受保护的混音(这保存在我的混音库):
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator class LoginRequiredMixin(object): @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
每当你想要一个视图被保护,你只需添加适当的mixin:
class SomeProtectedViewView(LoginRequiredMixin, TemplateView): template_name = 'index.html'
只要确保你的mixin是第一个。
更新:我在2011年发布了这个版本,从版本1.9开始,Django现在包含了这个和其他有用的混合(AccessMixin,PermissionRequiredMixin,UserPassesTestMixin)作为标准!
这是一个使用基于类的装饰器的替代方法:
from django.utils.decorators import method_decorator def class_view_decorator(function_decorator): """Convert a function based decorator into a class based decorator usable on class based Views. Can't subclass the `View` as it breaks inheritance (super in particular), so we monkey-patch instead. """ def simple_decorator(View): View.dispatch = method_decorator(function_decorator)(View.dispatch) return View return simple_decorator
这可以像这样简单地使用:
@class_view_decorator(login_required) class MyView(View): # this view now decorated
我意识到这个线程有点过时,但是这里是我的两分钱。
用下面的代码:
from django.utils.decorators import method_decorator from inspect import isfunction class _cbv_decorate(object): def __init__(self, dec): self.dec = method_decorator(dec) def __call__(self, obj): obj.dispatch = self.dec(obj.dispatch) return obj def patch_view_decorator(dec): def _conditional(view): if isfunction(view): return dec(view) return _cbv_decorate(dec)(view) return _conditional
我们现在有办法修补一个装饰器,所以它会变得多function。 这实际上意味着当应用于常规视图装饰器时,就像这样:
login_required = patch_view_decorator(login_required)
这个装饰器在使用原来的方式时仍然可以工作:
@login_required def foo(request): return HttpResponse('bar')
但使用时也会正常工作,如下所示:
@login_required class FooView(DetailView): model = Foo
在我最近遇到的几个案例中,这似乎工作正常,包括这个现实世界的例子:
@patch_view_decorator def ajax_view(view): def _inner(request, *args, **kwargs): if request.is_ajax(): return view(request, *args, **kwargs) else: raise Http404 return _inner
ajax_view函数是为了修改一个(基于函数的)视图而编写的,所以只要这个视图被非Ajax调用访问,就会引发一个404错误。 通过简单地使用补丁函数作为装饰器,这个装饰器也被设置为在基于视图的视图中工作
对于那些使用Django> = 1.9的人来说 ,它已经被包含在django.contrib.auth.mixins
像AccessMixin
, LoginRequiredMixin
, PermissionRequiredMixin
和UserPassesTestMixin
。
所以要将LoginRequired应用于CBV(例如DetailView
):
from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic.detail import DetailView class ViewSpaceIndex(LoginRequiredMixin, DetailView): model = Space template_name = 'spaces/space_index.html' login_url = '/login/' redirect_field_name = 'redirect_to'
记住GCBV Mixin命令也是很好的:Mixins必须在左边 ,而基本视图级必须在右边 。 如果订单不同,您可能会得到不可预知的结果。
如果是大多数页面要求用户login的网站,则可以使用中间件强制login所有视图, 但某些特别标记的视图除外 。
middleware.py:
from django.contrib.auth.decorators import login_required from django.conf import settings EXEMPT_URL_PREFIXES = getattr(settings, 'LOGIN_EXEMPT_URL_PREFIXES', ()) class LoginRequiredMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): path = request.path for exempt_url_prefix in EXEMPT_URL_PREFIXES: if path.startswith(exempt_url_prefix): return None is_login_required = getattr(view_func, 'login_required', True) if not is_login_required: return None return login_required(view_func)(request, *view_args, **view_kwargs)
views.py:
def public(request, *args, **kwargs): ... public.login_required = False class PublicView(View): ... public_view = PublicView.as_view() public_view.login_required = False
您不想包装的第三方视图可以在设置中进行优化:
settings.py:
LOGIN_EXEMPT_URL_PREFIXES = ('/login/', '/reset_password/')
使用Django大括号。 它提供了很多有用的mixin,很容易获得。 它有美丽的文档。 试试看。
你甚至可以创build你的自定义混合。
http://django-braces.readthedocs.org/en/v1.4.0/
示例代码:
from django.views.generic import TemplateView from braces.views import LoginRequiredMixin class SomeSecretView(LoginRequiredMixin, TemplateView): template_name = "path/to/template.html" #optional login_url = "/signup/" redirect_field_name = "hollaback" raise_exception = True def get(self, request): return self.render_to_response({})
在我的代码中,我已经写了这个适配器来使成员函数适应一个非成员函数:
from functools import wraps def method_decorator_adaptor(adapt_to,*decorator_args,**decorator_kwargs): def decorator_outer(func): @wraps(func) def decorator(self, *args,**kwargs): @adapt_to(*decorator_args,**decorator_kwargs) def adaptor(*args,**kwargs): return func(self, *args, **kwargs) return adaptor(*args,**kwargs) return decorator return decorator_outer
你可以像这样简单地使用它:
from django.http import HttpResponse from django.views.generic import View from django.contrib.auth.decorators import permission_required from some.where import method_decorator_adaptor class MyView(View): @method_decorator_adaptor(permission_required,'someapp.somepermission') def get(self, request): # <view logic> return HttpResponse('result')
如果你正在做一个需要各种许可testing的项目,你可以inheritance这个类。
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import user_passes_test from django.views.generic import View from django.utils.decorators import method_decorator class UserPassesTest(View): ''' Abstract base class for all views which require permission check. ''' requires_login = True requires_superuser = False login_url = '/login/' permission_checker = None # Pass your custom decorator to the 'permission_checker' # If you have a custom permission test @method_decorator(self.get_permission()) def dispatch(self, *args, **kwargs): return super(UserPassesTest, self).dispatch(*args, **kwargs) def get_permission(self): ''' Returns the decorator for permission check ''' if self.permission_checker: return self.permission_checker if requires_superuser and not self.requires_login: raise RuntimeError(( 'You have assigned requires_login as False' 'and requires_superuser as True.' " Don't do that!" )) elif requires_login and not requires_superuser: return login_required(login_url=self.login_url) elif requires_superuser: return user_passes_test(lambda u:u.is_superuser, login_url=self.login_url) else: return user_passes_test(lambda u:True)
我已经根据Josh的解决scheme进行了修复
class LoginRequiredMixin(object): @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
示例用法:
class EventsListView(LoginRequiredMixin, ListView): template_name = "events/list_events.html" model = Event