장고 클래스 기반 뷰에서 permission_required 데코레이터를 사용하는 방법


161

새 CBV의 작동 방식을 이해하는 데 약간의 어려움이 있습니다. 내 질문은 이것입니다. 모든보기와 특정 권한에 로그인해야합니다. 함수 기반 뷰에서 뷰의 @permission_required () 및 login_required 속성을 사용하여 수행하지만 새 뷰 에서이 작업을 수행하는 방법을 모르겠습니다. 장고 문서에 이것을 설명하는 섹션이 있습니까? 아무것도 찾지 못했습니다. 내 코드에 어떤 문제가 있습니까?

@method_decorator를 사용하려고했지만 " / spaces / prueba /의 TypeError _wrapped_view ()에서 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

답변:


211

CBV 문서 에는 다음과 같은 몇 가지 전략이 있습니다 .

보기 urls.py를 인스턴스화 할 때 인스턴스별로보기를 장식합니다 ( 문서 ).

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

데코레이터는 인스턴스별로 적용되므로 urls.py필요에 따라 다른 경로 에서 추가하거나 제거 할 수 있습니다 .

클래스를 꾸미면 뷰의 모든 인스턴스가 데코레이터 ( docs )에 의해 래핑됩니다.

이를 수행 할 수있는 두 가지 방법이 있습니다.

  1. method_decoratorCBV 디스패치 방법에 a 적용

    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)
  1. 현대 Django (2.2+)의 일반적인 관행은 django.contrib.auth.mixins.LoginRequiredMixin 과 같은 액세스 믹스 인 을 Django 1.9 이상에서 사용하고 여기에 다른 답변에서 잘 설명되어 있습니다.

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

상속 목록에서 Mixin을 먼저 배치해야합니다 (따라서 분석 방법 순서가 올바른 것을 선택 함).

당신이 얻는 이유 TypeError는 문서에 설명되어 있습니다 :

참고 : method_decorator는 * args 및 ** kwargs를 매개 변수로 클래스의 데코 레이팅 된 메소드에 전달합니다. 메소드가 호환 가능한 매개 변수 세트를 승인하지 않으면 TypeError 예외가 발생합니다.



추가하는 방법 message?
andilabs

이해하지 못하는 사람들을 위해 (처음에했던 것처럼) 'dispatch'메소드를 ViewSpaceIndex 클래스에 추가해야합니다
o_c

이러한 방법 중 하나를 다른 방법보다 선호하는 이유가 있습니까?
Alistair

@Alistair 개인적으로 선호하고 팀 / 조직 내에서 코드베이스 일관성을 유지한다고 생각합니다. 클래스 기반 뷰를 작성하는 경우 개인적으로 mixin 접근 방식을 선호합니다.
A Lee

118

다음은 접근 방식입니다. 보호되는 믹스 인을 만듭니다 (이것은 내 믹스 인 라이브러리에 유지됨)

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, PermissionRequiredMixin, UserPassesTestMixin)을 표준으로 포함합니다!


이런 종류의 믹스 인을 여러 개 가질 수 있습니까? 그것은 나를 위해 작동하지 않았고 그것이 그것이 의미가 있다고 생각하지 않습니다.
Pykler

네, 각 믹스 인은 MRO
Hobblin

나는 이것이 우아한 해결책이라고 생각한다. 내 urls.py에 데코레이터와 views.py에 믹스 인을 섞어 놓는 것을 좋아하지 않습니다. 이것은 모든 논리를 뷰로 옮기는 데코레이터를 감싸는 방법입니다.
dhackner

1
django-braces는 이것 (그리고 더 많은)
mixins을 가지고 있습니다

나 같은 완전 지연 모드에있는 사람들을위한 참고 사항 : login_required 기능을 테스트 할 때 로그인하지 않았는지 확인하십시오.
Visgean Skeloru

46

클래스 기반 데코레이터를 사용하는 대안은 다음과 같습니다.

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
이것을 사용하여 뷰 데코레이터를 멋지게 연결할 수 있습니다! +1
Pykler 2016 년

9
업스트림 IMO를 포함시키기 위해 고려해야 할 것이 너무 좋습니다.
koniiiik

나는 이것을 좋아한다! args / kwargs를 class_view_decorator에서 function_decorator로 전달하는 것이 가능한지 궁금합니다. login_decorator가 조건부로 요청과 일치한다고 말할 수 있다면 좋을 것입니다.
Mike Waits

1
args / kwargs는를 사용하여 쉽게 달성 할 수 있어야합니다 class_view_decorator(my_decorator(*args, **kwargs)). 조건부 메서드 일치에 대해서는 class_view_decorator를 수정하여 대신 적용 View.get하거나 View.post대신 적용 할 수 있습니다 View.dispatch.
mjtamlyn

14

나는이 스레드가 약간 오래되었다는 것을 알고 있지만 어쨌든 내 2 센트가 있습니다.

다음 코드로 :

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 오류가 발생합니다. 단순히 데코레이터로 패치 기능을 적용함으로써이 데코레이터는 모두 클래스 기반 뷰에서도 작동하도록 설정됩니다


14

사용하는 분들을 위해 > = 1.9 장고를 , 그것은 이미에 포함 django.contrib.auth.mixins으로 AccessMixin, LoginRequiredMixin, PermissionRequiredMixinUserPassesTestMixin.

따라서 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은 온 가야 왼쪽 측면 및 기본 뷰 클래스에 가야 바로 옆. 순서가 다르면 깨지거나 예측할 수없는 결과를 얻을 수 있습니다.


2
2019 년 최고의 답변입니다. 또한 믹스 인 순서에 대한 좋은 점입니다.
Christian Long

5

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({})

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

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

이것이 장고에 내장되어 있다는 것이 좋을 것 method_decorator입니다. 이것을 달성하는 훌륭하고 읽기 쉬운 방법 인 것 같습니다.
MariusSiuram

1

이는 1.9에 대한 지원과 함께 오는> 아주 쉽게 장고 함께 PermissionRequiredMixin하고LoginRequiredMixin

인증에서 가져 오기만하면됩니다.

views.py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

자세한 내용은 읽어 장고에 권한 부여


1

지금은 한참 지났고 지금은 장고가 너무 많이 바뀌 었습니다.

클래스 기반 뷰를 장식하는 방법은 여기를 확인하십시오.

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_requiredmethod_decorator

우리는 할 수 있습니다 :

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

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)

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

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.