Как использовать декораторы permission_required в представлениях на основе классов django

StackOverflow https://stackoverflow.com/questions/6069070

Вопрос

У меня возникли небольшие проблемы с пониманием того, как работают новые CBV.Мой вопрос заключается в следующем: мне нужно требовать входа во все представления, а в некоторых из них - определенных разрешений.В представлениях на основе функций я делаю это с помощью @permission_required() и атрибута login_required в представлении, но я не знаю, как это сделать в новых представлениях.Есть ли какой-нибудь раздел в документации django, объясняющий это?Я ничего не нашел.Что не так в моем коде?

Я попытался использовать @method_decorator, но он отвечает "TypeError в /spaces/prueba/ _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
Это было полезно?

Решение

Есть несколько стратегий, перечисленных в документы CBV:

Оформляйте представление для каждого экземпляра в своем urls.py когда вы создаете экземпляр своего представления (Документы)

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

Декоратор применяется для каждого экземпляра, поэтому вы можете добавлять или удалять его разными способами. urls.py прокладывает маршруты по мере необходимости.

Украсьте свой класс так, чтобы каждый экземпляр вашего представления был обернут декоратором (Документы)

Есть два способа, которыми вы можете это сделать:

  1. Применяя method_decorator к вашему методу отправки CBV, например,

    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.LOGINREQUIREREDMIXIN доступно в 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.

Другие советы

Вот мой подход, я создаю смесин, который защищен (это хранится в моей библиотеке 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)
.

Всякий раз, когда вы хотите, чтобы быть защищенным для защищений, вы просто добавляете соответствующую смеси:

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

Просто убедитесь, что ваш микн сначала.

Обновление: Я отправил это в обратно в 2011 году, начиная с версии 1.9 Django теперь включает в себя это и другие полезные смесины (AccessMixin, MembisisseRquireciremixin, UserPassestesteSteStemixin).

Вот альтернатива, используя классические декораторы:

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
.

Я понимаю, что эта нить немного датирована, но во всяком случае, мои два цента.

со следующим кодом:

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 записывается для модификации (на основе функций), чтобы он поднимал ошибку 404 всякий раз, когда этот вид посещается без AJAX.Простое применение функции патча в качестве декоратора, этот декоратор настроен для работы в классовых представлениях, а также

Для тех из вас, кто использует Джанго >= 1.9, это уже включено в django.contrib.auth.mixins как AccessMixin, LoginRequiredMixin, PermissionRequiredMixin и UserPassesTestMixin.

Таким образом, чтобы применить 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: Миксины должен идти по левый сторону, и базовый вид класс должен идти в право сторона.Если порядок отличается, вы можете получить неправильные и непредсказуемые результаты.

Используйте брекеты Django.Он обеспечивает много полезных смеси, которые легко доступны. У него красивые документы.Попробуй это.

Вы даже можете создать свои пользовательские смесины.

http://djang-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({})
.

Если это сайт, в котором большинство страниц требует, чтобы пользователь вошел в систему, вы можете использовать промежуточное программное обеспечение для запуска войти в все виды , кроме некоторых, которые особенно отмечены.

pre django 1.10 marmware.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) 
.

Просмотр .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/')
.

В моем коде я написал этот адаптер, чтобы адаптировать функции элементов к функции, не являющейся членами:

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> 1.9, приходящим с поддержкой PermissionRequiredMixin и LoginRequiredMixin

Просто импортировать из AUTH

Просмотр .py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass
.

Для более подробной информации read Авторизация в Django

Это было какое-то время, и теперь Django так сильно изменилось.

Проверьте здесь, для того, чтобы украсить вид на класс.

https://docs.djangoproject.com/ru/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):
        # ...
.

Если вы выполняете проект, который требует разнообразных тестов разрешений, вы можете наследовать этот класс.

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

Я сделал это исправление на основе решения Джоша

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
.

Здесь решение для разрешения_required Decorator:

class CustomerDetailView(generics.GenericAPIView):

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top