wake-up-neo.com

So verwenden Sie permission_required-Dekoratoren in Django-Ansichten

Ich habe Schwierigkeiten, die Funktionsweise der neuen CBVs zu verstehen. Meine Frage ist folgende: Ich muss in allen Ansichten und in einigen davon spezifische Berechtigungen anmelden. In funktionsbasierten Ansichten mache ich das mit @permission_required () und dem login_required-Attribut in der Ansicht, aber ich weiß nicht, wie ich dies mit den neuen Ansichten machen soll. Gibt es einen Abschnitt in den Django-Dokumenten, der dies erklärt? Ich habe nichts gefunden. Was ist falsch in meinem Code?

Ich habe versucht, den @ method_decorator zu verwenden, aber er antwortet " TypeError at/spaces/prueba/_wrapped_view () benötigt mindestens 1 Argument (0 gegeben) "

Hier ist der Code (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
136
Oscar Carballal

In den CBV-Dokumenten sind einige Strategien aufgeführt:

  1. Füge den Dekorator in deine urls.py-Route ein , z. B. login_required(ViewSpaceIndex.as_view(..)) 

  2. Verzieren Sie die dispatch-Methode Ihres CBV mit einem method_decorator z. B.

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

    Vor Django 1.9 können Sie method_decorator nicht für die Klasse verwenden. Daher müssen Sie die dispatch-Methode überschreiben: 

    class ViewSpaceIndex(TemplateView):
    
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
    
  3. Verwenden Sie die Zugriffsmixins wie Django.contrib.auth.mixins.LoginRequiredMixin , verfügbar in Django 1.9+ und in den anderen Antworten hier gut beschrieben:

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

Der Grund, warum Sie eine TypeError erhalten, wird in den Dokumenten erklärt:

Hinweis: method_decorator übergibt * args und ** kwargs als Parameter an die eingerichtete Methode der Klasse. Wenn Ihre Methode keine kompatiblen Parameter akzeptiert, wird eine TypeError-Ausnahme ausgelöst.

170
A Lee

Hier ist mein Ansatz. Ich erstelle ein Mixin, das geschützt ist (dies wird in meiner Mixin-Bibliothek aufbewahrt):

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)

Wann immer Sie eine Ansicht schützen möchten, fügen Sie einfach die entsprechende Mischung hinzu:

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

Stelle einfach sicher, dass dein Mixin an erster Stelle steht.

Update: Ich habe dies bereits 2011 veröffentlicht. Ab Version 1.9 enthält Django dieses und andere nützliche Mixins (AccessMixin, PermissionRequiredMixin, UserPassesTestMixin) standardmäßig!

114
Gert Steyn

Hier ist eine Alternative für klassenbasierte Dekorateure:

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

Dies kann dann einfach so verwendet werden:

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

Ich merke, dass dieser Thread ein bisschen veraltet ist, aber hier sind meine zwei Cents sowieso.

mit dem folgenden Code:

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

wir haben jetzt eine Möglichkeit, einen Dekorateur zu patchen, damit er multifunktional wird. Dies bedeutet effektiv, dass, wenn es auf einen regulären Ansichtsdekorateur angewendet wird, z.

login_required = patch_view_decorator(login_required)

dieser Dekorateur funktioniert auch dann, wenn er so verwendet wird, wie er ursprünglich vorgesehen war:

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

funktioniert aber auch richtig, wenn es so benutzt wird:

@login_required
class FooView(DetailView):
    model = Foo

Dies scheint in einigen Fällen gut zu funktionieren, auf die ich kürzlich gestoßen bin, einschließlich dieses Beispiels aus der Praxis:

@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

Die ajax_view-Funktion wird geschrieben, um eine (funktionsbasierte) Ansicht zu ändern, sodass sie einen Fehler 404 verursacht, wenn diese Ansicht von einem Nicht-Ajax-Aufruf besucht wird. Durch einfaches Anwenden der Patch-Funktion als Dekorateur kann dieser Dekorateur auch in klassenbasierten Ansichten arbeiten

14
mephisto

Für diejenigen, die Django> = 1.9 verwenden, ist es bereits in Django.contrib.auth.mixins als AccessMixin , LoginRequiredMixin , PermissionRequiredMixin und UserPassesTestMixin enthalten.

So wenden Sie LoginRequired an CBV an (z. B. 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'

Es ist auch gut, die Reihenfolge des GCBV-Mixing zu beachten: Mixins muss auf der Seite left gehen, und die Klasse Basisansicht muss auf der Seite right liegen . Wenn die Reihenfolge anders ist, können Sie fehlerhafte und unvorhersehbare Ergebnisse erhalten.

13
vishes_shell

Verwenden Sie Django-Zahnspangen. Es bietet viele nützliche Mixins, die leicht verfügbar sind. Es hat schöne Dokumente. Versuch es. 

Sie können sogar Ihre eigenen Mixins erstellen.

http://Django-braces.readthedocs.org/de/v1.4.0/

Beispielcode:

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
shap4th

Wenn es sich um eine Site handelt, bei der für die meisten Seiten die Anmeldung des Benutzers erforderlich ist, können Sie mithilfe einer Middleware die Anmeldung für alle Ansichten erzwingen außer einige, die besonders gekennzeichnet sind.

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

Ansichten von Drittanbietern, die Sie nicht umbrechen möchten, können in den Einstellungen ausgenommen werden:

settings.py:

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

In meinem Code habe ich diesen Adapter geschrieben, um Memberfunktionen an eine Nicht-Memberfunktion anzupassen:

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

Sie können es einfach so verwenden:

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')
3
rabbit.aaron

Dies ist sehr einfach, da Django> 1.9 mit PermissionRequiredMixin und LoginRequiredMixin unterstützt wird. 

Einfach aus der auth importieren 

views.py

from Django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

Für weitere Informationen lesen Sie Autorisierung in Django

1
Amar

Wenn Sie ein Projekt ausführen, für das verschiedene Berechtigungstests erforderlich sind, können Sie diese Klasse erben.

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)

Ich habe diesen Fix basierend auf Joshs Lösung gemacht

class LoginRequiredMixin(object):

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

Verwendungsbeispiel:

class EventsListView(LoginRequiredMixin, ListView):

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