我在理解新CBV如何工作方面遇到了一些麻烦。我的问题是,我需要登录所有视图,其中一些是特定权限。在基于函数的视图中,我使用@permission_required()和视图中的login_required属性执行此操作,但我不知道如何在新视图上执行此操作。 django文档中是否有一些部分解释了这一点?我没找到任何东西。我的代码出了什么问题?
我尝试使用@method_decorator,但它回复“ / errors / prueba / _wrapped_view()中的TypeError至少需要1个参数(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
答案 0 :(得分:174)
the CBV docs中列出了一些策略:
Add the decorator in your urls.py
route,例如login_required(ViewSpaceIndex.as_view(..))
Decorate your CBV's dispatch
method with a method_decorator
例如,
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之类的访问混合,并在其他答案中概述:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
您收到TypeError
的原因在文档中说明了:
注意: method_decorator将* args和** kwargs作为参数传递给类的修饰方法。如果您的方法不接受兼容的参数集,则会引发TypeError异常。
答案 1 :(得分:115)
这是我的方法,我创建了一个受保护的mixin(这保存在我的mixin库中):
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现在包括这个和其他有用的mixin(AccessMixin,PermissionRequiredMixin,UserPassesTestMixin)作为标准!
答案 2 :(得分:45)
这是使用基于类的装饰器的替代方法:
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
答案 3 :(得分:14)
我意识到这个帖子有点陈旧,但无论如何这里是我的两分钱。
使用以下代码:
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错误。通过简单地将补丁函数应用为装饰器,此装饰器也可以在基于类的视图中工作
答案 4 :(得分:14)
对于那些使用 Django> = 1.9 的人,它已作为AccessMixin
,LoginRequiredMixin
,PermissionRequiredMixin
包含在django.contrib.auth.mixins
中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 必须在左侧侧进行,基本视图类必须去在正确方面。如果订单不同,您可能会遇到破碎和不可预测的结果。
答案 5 :(得分:4)
如果它是大多数页面要求用户登录的站点,您可以使用中间件强制登录所有视图,除非特别标记的某些页面。
Pre Django 1.10 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/')
答案 6 :(得分:4)
使用Django Braces。它提供了许多易于获得的有用混合物。 它有漂亮的文档。试试看。
您甚至可以创建自定义混音。
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({})
答案 7 :(得分:3)
在我的代码中,我编写了这个适配器,以使成员函数适应非成员函数:
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')
答案 8 :(得分:1)
使用django&gt;这非常简单1.9支持PermissionRequiredMixin
和LoginRequiredMixin
只需从身份验证
导入即可views.py
from django.contrib.auth.mixins import LoginRequiredMixin
class YourListView(LoginRequiredMixin, Views):
pass
有关详细信息,请参阅Authorization in django
答案 9 :(得分:0)
如果您正在执行需要各种权限测试的项目,则可以继承此类。
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)
答案 10 :(得分:0)
我已根据Josh的解决方案进行了修复
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
答案 11 :(得分:0)
已经有一段时间了,现在Django发生了很大变化。
在此处检查如何装饰基于类的视图。
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
类似地,将permission_required
与method_decorator
一起使用
我们可以做到:
@method_decorator(permission_required("polls.can_vote"), name="dispatch")
class MyView:
def get(self, request):
# ...
答案 12 :(得分:0)
这是Permission_required装饰器的解决方案:
class CustomerDetailView(generics.GenericAPIView):
@method_decorator(permission_required('app_name.permission_codename', raise_exception=True))
def post(self, request):
# code...
return True