djangoクラスベースのビューでpermission_requiredデコレーターを使用する方法


161

新しいCBVのしくみを理解するのに少し問題があります。私の質問はこれです。すべてのビューでログインを要求する必要があります。いくつかのビューでは、特定の権限が必要です。関数ベースのビューでは、@ permission_required()とビューのlogin_required属性を使用してこれを行いますが、新しいビューでこれを行う方法がわかりません。これを説明するdjangoドキュメントにいくつかのセクションがありますか?何も見つかりませんでした。私のコードの何が問題になっていますか?

@method_decoratorを使用しようとしましたが、「/ spaces / 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

回答:


211

CBVドキュメントにリストされいるいくつかの戦略があります:

ビューurls.pyをインスタンス化するときに、インスタンスごとにビューを装飾します(docs

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

デコレータはインスタンスごとに適用されるためurls.py、必要に応じて別のルートでデコレータを追加または削除できます。

ビューのすべてのインスタンスがデコレーターによってラップされるようにクラスを装飾します(docs

これを行うには、2つの方法があります。

  1. method_decoratorCBVディスパッチメソッドにを適用します。

    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. 現代のジャンゴ(2.2以降)での一般的な方法は次のようにアクセスミックスインを使用することであるdjango.contrib.auth.mixins.LoginRequiredMixinジャンゴ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する方法?
アンディラブス2014年

理解していなかった人のために(私が最初に理解したように)-'dispatch'メソッドをViewSpaceIndexクラスに追加する必要があります
o_c

これらの方法のいずれかを他よりも優先する理由はありますか?
Alistair

@Alistairそれは結局、個人の好みとチーム/組織内のコードベースの一貫性を維持することになると思います。個人的には、クラスベースのビューを構築している場合は、ミックスインアプローチを採用する傾向があります。
A・リー

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)が標準で含まれています。


この種類のミックスインを複数持つことは可能ですか?それは私にとってはうまくいきませんでしたし、それが理にかなっているとは思いません。
ピクラー、

はい。各MixinがMROに従って次のクラスを選択するsuperへの呼び出しを行うため、複数のMixinを使用できるはずです
Hobblin

これはエレガントな解決策だと思います。urls.pyにデコレータを組み合わせ、views.pyにミックスインを組み合わせたくない これは、そのロジックのすべてをビューに移動するデコレータをラップする方法です。
dhackner 2013年

1
ジャンゴ・ブレースこれを持っている(そしてもっと)ミックスイン-インストールするには、非常に便利なパッケージ
askvictor

私のようなフルリタードモードの人へのメモ:login_required機能をテストするときは、ログインしていないことを確認してください...
Visgean Skeloru 2015年

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
ピクラー

9
これは非常に優れているため、上流のIMOに含めることを検討する必要があります。
koniiiik 2012年

これ大好き!args / kwargsをclass_view_decoratorからfunction_decoratorに渡すことはまったく可能ですか?login_decoratorが条件付き一致のrequest.METHODと言うことができれば、それはsayポストにのみ適用されますか?
マイクウェイツ2013

1
args / kwargsは、を使用して簡単に実現できclass_view_decorator(my_decorator(*args, **kwargs))ます。条件付きメソッドマッチングについては、class_view_decoratorを変更して、に適用するView.getか、のView.post代わりに適用することができますView.dispatch
mjtamlyn 2013

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に、それは既にに含まれているdjango.contrib.auth.mixinsとしてAccessMixinLoginRequiredMixinPermissionRequiredMixinUserPassesTestMixin

したがって、LoginRequiredをCBV(eg 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 ミックスインの順序を覚えておくことも良いです。ミックスイン左側に配置し、ベースビュークラスは右側に配置する必要があります。順序が異なる場合は、壊れて予期しない結果になる可能性があります。


2
これは2019年のベストアンサーです。また、ミックスインの順序についての大きなポイントです。
クリスチャンロング

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

ほとんどのページでユーザーがログインする必要があるサイトの場合、ミドルウェアを使用して、特にマークされているビューを除くすべてのビューにログインを強制できます。

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

これがDjangoに組み込まれているといいのですが(そのままですmethod_decorator)。これは、これを実現するための優れた読みやすい方法のようです。
MariusSiuram、2015年

1

これはdjangoで非常に簡単です> 1.9とのサポートが付属しPermissionRequiredMixinていますLoginRequiredMixin

認証からインポートするだけです

views.py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

詳細については、djangoでの承認をご覧ください。


1

久しぶりに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):
        # ...

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

私はジョシュのソリューションに基づいてその修正を行いました

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.