如何在基于 django 类的视图上使用权限 _ 必需的装饰符

我不太明白新的 CBV 是怎么运作的。我的问题是,我需要登录所有的视图,并在其中一些,具体的权限。在基于函数的视图中,我在视图中使用@mission _  ()和 login _  属性来完成这项工作,但是我不知道如何在新视图中完成这项工作。姜戈文档里有解释吗?我什么都没找到。我的代码出了什么问题?

我尝试使用@method _ decorator,但它回答“ /space/prueba/_  包裝 _ view ()的 TypeError 至少有一個引數(0已知)

下面是代码(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
115108 次浏览

CBV 的文件中列出了一些策略:

urls.py(医生)中实例化视图时装饰它

from django.contrib.auth.decorators import login_required


urlpatterns = [
path('view/',login_required(ViewSpaceIndex.as_view(..)),
...
]

修饰符是在每个实例的基础上应用的,因此您可以根据需要在不同的 urls.py路由中添加或删除它。

装饰您的类,以便包装视图的每个实例(医生)

有两种方法可以做到这一点:

  1. method_decorator应用于 CBV 分派方法,例如,

     from django.utils.decorators import method_decorator
    from django.contrib.auth.decorators import login_required
    
    
    @method_decorator(login_required, name='dispatch')
    class ViewSpaceIndex(TemplateView):
    template_name = 'secret.html'
    

如果您使用的是 Django < 1.9(不应该这样做,因为它不再受支持) ,那么您就不能在类上使用 method_decorator,因此您必须手动重写 dispatch方法:

    from django.contrib.auth.decorators import login_required


class ViewSpaceIndex(TemplateView):


@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
  1. 使用类似 Django.contrib.auth.mixins. LoginRequiredMixin这样的混合函数,在这里的其他答案中可以很好地描述:

     from django.contrib.auth.mixins import LoginRequiredMixin
    
    
    class MyView(LoginRequiredMixin, View):
    
    
    login_url = '/login/'
    redirect_field_name = 'redirect_to'
    

确保将混合类放在继承列表的第一位(因此 Python 的 方法分解顺序算法选择了正确的事情)。

你得到 TypeError的原因在文件中有解释:

注: Method _ decorator 将 * args 和 * * kwargs 作为参数传递给类上的修饰方法。如果您的方法不接受一组兼容的参数,它将引发 TypeError 异常。

下面是我的方法,我创建了一个受保护的混合文件(它保存在我的混合文件库中) :

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)

无论何时你想要保护一个视图,你只需要添加适当的混合:

class SomeProtectedViewView(LoginRequiredMixin, TemplateView):
template_name = 'index.html'

只要确保你的搅拌器是第一个。

更新: 我早在2011年就发布了这篇文章,从1.9版本开始 Django 现在包含了这个和其他有用的混合文件(AccessMixin,PermisonRequredMixin,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

我们现在有了一个修补装饰器的方法,所以它将成为多功能的。这实际上意味着,当应用到常规视图装饰器时,如下所示:

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错误。通过简单地应用补丁函数作为装饰器,这个装饰器也可以在基于类的视图中工作

如果是一个大多数页面需要用户登录的站点,您可以使用一个中间件强制登录所有视图 除了中一些特别标记的视图。

Pre Django 1.10 midware.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)

视图:

def public(request, *args, **kwargs):
...
public.login_required = False


class PublicView(View):
...
public_view = PublicView.as_view()
public_view.login_required = False

您不想包装的第三方视图可以在以下设置中例外:

设置:

LOGIN_EXEMPT_URL_PREFIXES = ('/login/', '/reset_password/')

使用 Django 大括号。它提供了很多容易获得的有用的混合函数。 那里的文件很漂亮,试试吧。

您甚至可以创建自定义混合器。

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')

如果您正在执行需要各种权限测试的项目,则可以继承此类。

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)

我根据乔什的解决方案做了修正

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

对于那些使用 姜戈 > = 1.9的人来说,它已经作为 AccessMixinLoginRequiredMixinPermissionRequiredMixinUserPassesTestMixin包含在 django.contrib.auth.mixins中了。

因此,应用登录到 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 的顺序也是很好的: 混音器必须走在 左边的一边,而 底部视图类必须走在 的一边。如果顺序不同,你可能会得到破碎和不可预测的结果。

Django > 1.9支持 PermissionRequiredMixinLoginRequiredMixin,这非常简单

只是从作者那里进口

视野,视野

from django.contrib.auth.mixins import LoginRequiredMixin


class YourListView(LoginRequiredMixin, Views):
pass

更多细节请阅读 姜戈授权

已经有一段时间了,现在姜戈变了很多。

有关如何装饰基于类的视图,请查看此处。

Https://docs.djangoproject.com/en/2.2/topics/class-based-views/intro/#decorating-the-class

文档中没有包括“采用任何参数的装饰器”的示例。但是参与讨论的装饰师是这样的:

def mydec(arg1):
def decorator(func):
def decorated(*args, **kwargs):
return func(*args, **kwargs) + arg1
return decorated
return deocrator

因此,如果我们要使用 mydec 作为一个没有参数的“普通”修饰符,我们可以这样做:

mydecorator = mydec(10)


@mydecorator
def myfunc():
return 5

类似地,对 method_decorator使用 permission_required

我们可以做到:

@method_decorator(permission_required("polls.can_vote"), name="dispatch")
class MyView:
def get(self, request):
# ...

这里有许可 _ 必需装饰器的解决方案:

class CustomerDetailView(generics.GenericAPIView):


@method_decorator(permission_required('app_name.permission_codename', raise_exception=True))
def post(self, request):
# code...
return True