كيفية استخدام أدوات الديكور التي تتطلب إذنًا في طرق العرض المستندة إلى فئة Django

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

سؤال

أواجه بعض المشاكل في فهم كيفية عمل CBVs الجديدة.سؤالي هو: أحتاج إلى طلب تسجيل الدخول في جميع طرق العرض، وفي بعضها، أذونات محددة.في طرق العرض المستندة إلى الوظيفة، أفعل ذلك باستخدام @permission_required() والسمةlogin_required في طريقة العرض، لكنني لا أعرف كيفية القيام بذلك في طرق العرض الجديدة.هل هناك قسم ما في مستندات جانغو يشرح ذلك؟لم أجد أي شيء.ما الخطا في الكود الخاص بي؟

لقد حاولت استخدام @method_decorator ولكنه يرد "خطأ TypeError في /spaces/prueba/ _wrapped_view() يأخذ وسيطة واحدة على الأقل (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.LoginRequiredMixin متوفر في 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، وPermissionRequiredMixin، وUserPassesTestMixin) كمعيار قياسي!

إليك بديل باستخدام أدوات الديكور القائمة على الفصل:

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.

لذلك لتطبيق تسجيل الدخول المطلوب على 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 Mixin: ميكسينز يجب أن تذهب على غادر الجانب، و عرض القاعدة يجب أن يذهب الفصل في يمين جانب.إذا كان الترتيب مختلفًا، فيمكنك الحصول على نتائج مكسورة وغير متوقعة.

استخدم أقواس جانغو.يوفر الكثير من الخلطات المفيدة المتوفرة بسهولة.لديها مستندات جميلة.حاول.

يمكنك حتى إنشاء mixins المخصصة الخاصة بك.

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

إذا كان موقعًا حيث تتطلب غالبية الصفحات تسجيل دخول المستخدم، فيمكنك استخدام برنامج وسيط لفرض تسجيل الدخول على جميع طرق العرض يستثني بعض الذين تم وضع علامة خاصة عليهم.

ما قبل جانغو 1.10 الوسيطة.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

يمكن استثناء عروض الجهات الخارجية التي لا ترغب في تضمينها في الإعدادات:

إعدادات.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

فقط قم بالاستيراد من المصادقة

وجهات النظر.py

from django.contrib.auth.mixins import LoginRequiredMixin

class YourListView(LoginRequiredMixin, Views):
    pass

لمزيد من التفاصيل اقرأ إذن في جانغو

لقد مر وقت طويل والآن تغير جانغو كثيرًا.

تحقق هنا لمعرفة كيفية تزيين طريقة العرض على أساس الفصل الدراسي.

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

إذا كنت تقوم بمشروع يتطلب مجموعة متنوعة من اختبارات الأذونات، فيمكنك وراثة هذه الفئة.

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

هنا الحل لمصمم الديكور الذي يتطلب إذنًا:

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