Django sınıfı tabanlı görünümlerde allow_required dekoratörleri kullanma


161

Yeni CBV'lerin nasıl çalıştığını anlamakta biraz zorlanıyorum. Benim sorum şu, tüm görünümlerde ve bazılarında belirli izinler giriş gerekir gerekir. İşlev tabanlı görünümlerde bunu @permission_required () ve login_required özniteliğiyle görünümde yaparım, ancak bunu yeni görünümlerde nasıl yapacağımı bilmiyorum. Django belgelerinde bunu açıklayan bir bölüm var mı? Hiçbir şey bulamadım. Kodumda yanlış olan ne?

@Method_decorator kullanmaya çalıştım ama " / spaces / prueba / _wrapped_view () türünde TypeError en az 1 bağımsız değişken alır (0 verilir) "

İşte kod (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

Yanıtlar:


211

CBV belgelerinde listelenen birkaç strateji vardır :

Görünümü urls.pyörneklendirdiğinizde, görünümünüzü başlatırken ( dokümanlar )

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

Dekoratör, örnek başına uygulanır, böylece onu ekleyebilir veya urls.pygerektiğinde farklı yollardan kaldırabilirsiniz .

Sınıfınızı, görünümünüzün her örneğinin dekoratör ( docs ) tarafından sarılması için süsleyin

Bunu iki şekilde yapabilirsiniz:

  1. method_decoratorCBV gönderim yönteminize a uygulamak , ör.

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

Django <1.9 kullanıyorsanız (bunu yapmamalısınız, artık desteklenmemektedir) method_decoratorsınıfta kullanamazsınız , bu nedenle dispatchyöntemi geçersiz kılmanız gerekir :

    class ViewSpaceIndex(TemplateView):

        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
  1. Modern Django'da (2.2+) yaygın bir uygulama, Django 1.9+ 'da bulunan ve buradaki diğer cevaplarda iyi özetlenen django.contrib.auth.mixins.LoginRequiredMixin gibi erişim karışımlarını kullanmaktır :

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

Mixin'i önce miras listesine koyduğunuzdan emin olun (böylece Yöntem Çözüm Düzeni Sağ Şey'i alır).

A alma nedeniniz TypeErrordokümanlarda açıklanmaktadır:

Not: method_decorator, * args ve ** kwargs öğelerini sınıftaki dekore edilmiş yönteme parametre olarak geçirir. Yönteminiz uyumlu bir parametre kümesini kabul etmezse, TypeError istisnasını yükseltir.



nasıl eklenir message?
andilabs

Anlamayanlar için (ilk yaptığım gibi) - 'gönderme' yöntemi ViewSpaceIndex sınıfına
eklenmelidir

Bu yöntemlerden birini diğerine tercih etmek için herhangi bir neden var mı?
Alistair

@Alistair Kişisel tercihinize ve ekibiniz / kuruluşunuzdaki kod tabanı tutarlılığını korumaya bağlı olduğunu düşünüyorum. Sınıf temelli görüşler oluşturuyorsam şahsen mixin yaklaşımına yöneliyorum.
A Lee

118

İşte benim yaklaşım, korunan bir mixin oluşturmak (bu benim mixin kütüphanesinde tutulur):

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)

Bir görünümün korunmasını istediğinizde, uygun karışımı eklemeniz yeterlidir:

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

Sadece karışımınızın önce olduğundan emin olun.

Güncelleme: Bunu 1.9 yılında yayınladım, 1.9 sürümünden başlayarak Django şimdi bu ve diğer yararlı karışımları (AccessMixin, PermissionRequiredMixin, UserPassesTestMixin) standart olarak içeriyor!


bu tür birçok karışımın olması mümkün müdür? Benim için işe yaramadı ve ben de bunun mantıklı olacağını düşünmüyorum.
Pykler

Her mixin MRO uyarınca bir sonraki sınıf seçer süper bir çağrı yapar çünkü Evet birkaç katmalar olması mümkün olmalıdır
Hobblin

Bunun zarif bir çözüm olduğunu düşünüyorum; Urls.py dosyamda dekoratörler ve views.py dosyasındaki mixins karışımını sevmiyorum. Bu, tüm bu mantığı görünüme taşıyacak dekoratörleri sarmanın bir yoludur.
04:28

1
django-braces bu (ve daha fazlası) mixins var - yüklemek için çok yararlı bir paket
askvictor

Benim gibi tam geciktirme modunda insanlar için sadece bir not: login_required işlevselliğini test ederken giriş yapmadığınızdan emin olun ...
Visgean Skeloru

46

İşte sınıf tabanlı dekoratörler kullanan bir alternatif:

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

Bu daha sonra şu şekilde kullanılabilir:

@class_view_decorator(login_required)
class MyView(View):
    # this view now decorated

3
Bu görünümü dekoratörleri zincirlemek için güzel kullanabilirsiniz! +1
Pykler

9
Bu o kadar büyük ki, yukarı IMO'ya dahil edilmek için düşünülmelidir.
koniiiik

Bunu seviyorum! Im merak args / kwargs class_view_decorator işlev_decorator aşağı geçmek mümkün mü ??? Login_decorator, koşullu olarak request.METHOD koşulunu eşleştirebilseydi harika olurdu, bu yüzden sadece söz konusu yazı için geçerlidir?
Mike Waites

1
Args / kwarg'lar kullanılarak kolayca erişilebilir olmalıdır class_view_decorator(my_decorator(*args, **kwargs)). Koşullu yöntem eşleşmesine gelince - class_view_decorator öğesini kendisini uygulamak yerine View.getveya View.postyerine uygulamak üzere değiştirebilirsiniz View.dispatch.
mjtamlyn

14

Bu konu biraz tarihli, ama işte benim iki sent zaten fark.

aşağıdaki kodla:

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

şimdi bir dekoratörü yamalamak için bir yolumuz var, bu yüzden çok fonksiyonlu hale gelecek. Bu, düzenli bir görünüm dekoratörüne uygulandığında, şöyle etkili bir şekilde anlamına gelir:

login_required = patch_view_decorator(login_required)

bu dekoratör başlangıçta amaçlandığı şekilde kullanıldığında hala çalışacaktır:

@login_required
def foo(request):
    return HttpResponse('bar')

ancak şu şekilde kullanıldığında da düzgün çalışır:

@login_required
class FooView(DetailView):
    model = Foo

Bu, gerçek dünya örneği de dahil olmak üzere, son zamanlarda karşılaştığım birkaç durumda iyi çalışıyor gibi görünüyor:

@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 işlevi, (işlev tabanlı) bir görünümü değiştirmek için yazılır, böylece bu görünüm ajax olmayan bir çağrı tarafından her ziyaret edildiğinde 404 hatası oluşturur. Düzeltme eki işlevini yalnızca dekoratör olarak uygulayarak, bu dekoratör, sınıf tabanlı görünümlerde de çalışmaya hazırdır


14

Kullanan olanlarınız için > = 1.9 Django , zaten dahil oluyor django.contrib.auth.mixinsolarak AccessMixin, LoginRequiredMixin, PermissionRequiredMixinve UserPassesTestMixin.

CBR'ye LoginRequired uygulamak için (ör. 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'

Zihni GCBV Mixin sırayla tutmak için de iyi: Katmalar devam etmeli sol tarafında ve taban görünümü sınıf girmeli sağ tarafında. Sipariş farklıysa, kırık ve öngörülemeyen sonuçlar alabilirsiniz.


2
Bu 2019'daki en iyi cevaptır. Ayrıca, mixin siparişi hakkında harika bir nokta.
Christian Long

5

Django Parantez kullanın. Kolayca bulunabilen birçok yararlı karışım sağlar. Güzel dokümanlar var. Denemek.

Özel karışımlarınızı bile oluşturabilirsiniz.

http://django-braces.readthedocs.org/en/v1.4.0/

Örnek Kod:

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

Sayfaların çoğunun kullanıcının oturum açmasını gerektiren bir siteyse , özellikle işaretlenmiş olanlar dışındaki tüm görünümlerde oturum açmaya zorlamak için bir ara katman yazılımı kullanabilirsiniz .

Django 1.10 öncesi 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

Sarmak istemediğiniz üçüncü taraf görünümleri ayarlarda muaf tutulabilir:

settings.py:

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

3

Kodumda üye işlevlerini üye olmayan bir işleve uyarlamak için bu bağdaştırıcıyı yazdım:

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

Basitçe şöyle kullanabilirsiniz:

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

Bunun Django'da yerleşik olması güzel olurdu (aynen olduğu gibi method_decorator). Bunu başarmanın hoş ve okunaklı bir yolu gibi görünüyor.
MariusSiuram

1

Django> 1.9 için destek ile gelen bu çok kolay PermissionRequiredMixinveLoginRequiredMixin

Sadece yetkiden içe aktar

views.py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

Daha fazla bilgi için django'da Yetkilendirme bölümünü okuyun


1

Bir süredir oldu ve şimdi Django çok değişti.

Sınıf tabanlı bir görünümün nasıl dekore edileceğini görmek için buraya bakın.

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

Dokümantasyonda "herhangi bir argüman alan dekoratörler" örneği yoktu. Ancak argümanlar alan dekoratörler şöyle:

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

mydec'i argüman olmadan "normal" bir dekoratör olarak kullanacaksak, bunu yapabiliriz:

mydecorator = mydec(10)

@mydecorator
def myfunc():
    return 5

Benzer bir şekilde, kullanımı permission_requiredilemethod_decorator

Biz yapabiliriz:

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

0

Çeşitli izin testleri gerektiren bir proje yapıyorsanız, bu sınıfı devralabilirsiniz.

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

Bu düzeltmeyi Josh'un çözümüne dayanarak yaptım

class LoginRequiredMixin(object):

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)

Örnek kullanım:

class EventsListView(LoginRequiredMixin, ListView):

    template_name = "events/list_events.html"
    model = Event

0

İşte perm_required dekoratör için çözüm:

class CustomerDetailView(generics.GenericAPIView):

@method_decorator(permission_required('app_name.permission_codename', raise_exception=True))
    def post(self, request):
        # code...
        return True
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.