Question

Je vais avoir un peu de mal à comprendre comment le nouveau CBVs travail.Ma question est la suivante, j'ai besoin d'exiger de connexion dans tous les points de vue, et dans certains d'entre eux, des autorisations spécifiques.En fonction des vues basées sur les je le fais avec @permission_required() et le login_required attribut de la vue, mais je ne sais pas comment le faire sur le nouveau point de vue.Est-il un article dans le django docs expliquer cela?Je n'ai pas trouvé quoi que ce soit.Quel est le problème dans mon code?

J'ai essayé d'utiliser le @method_decorator mais elle rétorque "TypeError à /espaces/prueba/ _wrapped_view() prend au moins 1 argument (0)"

Voici le 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
Était-ce utile?

La solution

Il ya quelques stratégies énumérées dans la CBV docs:

Décorer la vue par exemple, dans votre urls.py lorsque vous instanciez votre point de vue (docs)

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

Le décorateur est appliquée par exemple de base, de sorte que vous pouvez ajouter ou supprimer des différentes urls.py les itinéraires en tant que de besoin.

Décorer votre classe, de sorte que chaque instance de votre point de vue sera enveloppé par le décorateur (docs)

Il y a deux façons dont vous pouvez faire ceci:

  1. L'application d'un method_decorator pour votre CBV méthode dispatch par exemple,

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

Si vous êtes à l'aide de Django < 1.9 (qui ne convient pas, il n'est plus pris en charge), vous ne pouvez pas utiliser method_decorator sur la classe, de sorte que vous devez remplacer la dispatch méthode:

    class ViewSpaceIndex(TemplateView):

        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
  1. Une pratique courante dans moderne Django (2.2+) est d'utiliser l'accès mixin comme django.contrib.auth.mixin.LoginRequiredMixin disponible dans Django de 1,9+ et décrit bien dans les autres réponses ici:

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

Assurez-vous de mettre le Mixin en premier dans la liste d'héritage (donc la Méthode de Résolution de l'Ordre choisit la bonne Chose).

La raison pour laquelle vous êtes l'obtention d'un TypeError est expliqué dans la doc:

Note:method_decorator passe *args et **kwargs comme paramètres à la décorées méthode de la classe.Si votre méthode n'accepte pas compatible d'un ensemble de paramètres qu'il va lever une exception TypeError.

Autres conseils

Voici mon approche, je crée un mixin protégé (ceci est conservé dans ma bibliothèque 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)

Chaque fois que vous voulez que la vue soit protégée, vous devez simplement ajouter le mixin approprié:

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

Assurez-vous que votre mixin est le premier.

Mise à jour: J'ai posté cette question de retour en 2011, en commençant par la version 1.9 Django inclut maintenant ceci et d'autres mélanges utiles (AccessMixin, PermissionRequiredMixin, userPassestestMixin) en standard!

Voici une alternative utilisant des décorateurs de classe:

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

Ceci peut ensuite être utilisé simplement comme ceci:

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

Je réalise que ce fil est un peu daté, mais voici mes deux cents de toute façon.

avec le code suivant:

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

Nous avons maintenant un moyen de corriger un décorateur, il deviendra donc multifonctionnel.Cela signifie effectivement que lorsqu'il est appliqué à un décorateur de vue régulier, comme:

login_required = patch_view_decorator(login_required)

Ce décorateur fonctionnera toujours lorsqu'il sera utilisé comme il était initialement destiné à:

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

mais fonctionnera également correctement lorsqu'il est utilisé comme:

@login_required
class FooView(DetailView):
    model = Foo

Cela semble fonctionner bien dans plusieurs cas que j'ai récemment rencontré, y compris cet exemple du monde réel:

@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

La fonction AJAX_View est écrite pour modifier une vue (fonction basée sur la fonction), de sorte qu'elle augmente une erreur 404 chaque fois que cette vue est visitée par un appel non Ajax.En appliquant simplement la fonction de correctif sous forme de décorateur, ce décorateur est entièrement défini pour fonctionner en vues basées sur la classe aussi bien

Pour ceux d'entre vous qui les utilisent Django >= 1.9, il est déjà inclus dans django.contrib.auth.mixins comme AccessMixin, LoginRequiredMixin, PermissionRequiredMixin et UserPassesTestMixin.

Donc, pour appliquer LoginRequired de CBV(par ex. 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'

Il est également bon de garder à l'esprit GCBV Mixin ordre: Mixin faut aller sur le de gauche côté, et de l' vue de la base la classe doit aller dans le droit côté.Si l'ordre est différent, vous pouvez obtenir cassé et des résultats imprévisibles.

Utilisez des accolades Django.Il fournit de nombreux mélanges utiles facilement disponibles. Il a de belles documents.Essaye le.

Vous pouvez même créer vos mixines personnalisées.

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

Exemple de code:

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

Si c'est un site où la majorité des pages nécessite l'enregistrement de l'utilisateur, vous pouvez utiliser un middleware pour forcer la connexion sur toutes les vues sauf les certains qui sont particulièrement marqués.

Pré-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) 

vues.py:

def public(request, *args, **kwargs):
    ...
public.login_required = False

class PublicView(View):
    ...
public_view = PublicView.as_view()
public_view.login_required = False

Vues tierces que vous ne voulez pas envelopper peut être fabriquée dans les réglages:

paramètres.py:

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

Dans mon code, j'ai écrit cet adaptateur pour adapter les fonctions des membres à une fonction non membre:

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

Vous pouvez simplement l'utiliser comme ceci:

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

C'est super facile avec django > 1.9 à venir, avec le soutien de PermissionRequiredMixin et LoginRequiredMixin

Il suffit d'importer de l'auth

views.py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

Pour plus de détails, lire Autorisation dans django

Il a été un certain temps maintenant, et maintenant, Django a beaucoup changé.

Vérifiez ici pour savoir comment décorer une classe de base point de vue.

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

La documentation ne soit pas un exemple de "décorateurs qui prend un argument".Mais décorateurs prendre des arguments sont comme ceci:

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

donc, si nous sommes à l'utilisation mydec comme "normale" décorateur, sans arguments, nous pouvons faire ceci:

mydecorator = mydec(10)

@mydecorator
def myfunc():
    return 5

Donc de la même façon, à utiliser permission_required avec method_decorator

nous pouvons faire:

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

Si vous faites un projet qui nécessite une variété de tests d'autorisation, vous pouvez hériter de cette classe.

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)

J'ai fait ce correctif basé sur la solution de Josh

class LoginRequiredMixin(object):

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

Utilisation des échantillons:

class EventsListView(LoginRequiredMixin, ListView):

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

Ici la solution pour la permission_Required décorateur:

class CustomerDetailView(generics.GenericAPIView):

@method_decorator(permission_required('app_name.permission_codename', raise_exception=True))
    def post(self, request):
        # code...
        return True

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top