복붙노트

[PYTHON] 장고의 login_required를 만드는 가장 좋은 방법은 기본값입니다.

PYTHON

장고의 login_required를 만드는 가장 좋은 방법은 기본값입니다.

나는 대형 Django 응용 프로그램을 개발 중입니다. 대다수의 응용 프로그램은 액세스하기 위해 로그인해야합니다. 즉, 앱 전체에 뿌려졌습니다.

@login_required
def view(...):

괜찮아요. 우리가 사방에 그것을 추가하는 것을 잊지 않는 한 훌륭하게 작동합니다! 슬프게도 때로는 잊어 버리고 실패는 종종 끔찍하게 드러나지 않습니다. 보기로 연결되는 유일한 링크가 @login_required 페이지에 있으면 로그인하지 않고도 해당보기에 실제로 도달 할 수 있음을 알지 못할 것입니다. 그러나 나쁜 사람이 알아 차릴 수 있습니다. 이는 문제입니다.

내 생각은 시스템을 뒤집는 것이었다. @login_required를 사방에 입력하는 대신, 대신 다음과 같이 할 수 있습니다.

@public
def public_view(...):

그냥 대중을 위해. 나는 이것을 약간의 미들웨어로 구현하려했으나 제대로 작동하지 않는 것처럼 보였다. 시도한 모든 것이 우리가 사용하는 다른 미들웨어와 나쁘게 상호 작용했다고 생각합니다. 다음으로 나는 @public이 아닌 모든 것이 @login_required로 표시되었는지 확인하기 위해 URL 패턴을 가로 지르는 무언가를 써 보았습니다. 적어도 우리가 뭔가를 잊어 버리면 빠른 오류를 얻을 것입니다. 그런데 어떻게하면 @login_required가 뷰에 적용되었는지 알 수있는 방법을 찾지 못했습니다 ...

그래서, 이것을하는 올바른 방법은 무엇입니까? 도와 주셔서 감사합니다!

해결법

  1. ==============================

    1.미들웨어가 최선의 방법 일 수 있습니다. 과거에이 코드를 사용했고, 다른 곳에서 발견 된 스 니펫에서 수정했습니다.

    미들웨어가 최선의 방법 일 수 있습니다. 과거에이 코드를 사용했고, 다른 곳에서 발견 된 스 니펫에서 수정했습니다.

    import re
    
    from django.conf import settings
    from django.contrib.auth.decorators import login_required
    
    
    class RequireLoginMiddleware(object):
        """
        Middleware component that wraps the login_required decorator around
        matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
        define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
        settings.py. For example:
        ------
        LOGIN_REQUIRED_URLS = (
            r'/topsecret/(.*)$',
        )
        LOGIN_REQUIRED_URLS_EXCEPTIONS = (
            r'/topsecret/login(.*)$',
            r'/topsecret/logout(.*)$',
        )
        ------
        LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
        be a valid regex.
    
        LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
        define any exceptions (like login and logout URLs).
        """
        def __init__(self):
            self.required = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS)
            self.exceptions = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            # No need to process URLs if user already logged in
            if request.user.is_authenticated():
                return None
    
            # An exception match should immediately return None
            for url in self.exceptions:
                if url.match(request.path):
                    return None
    
            # Requests matching a restricted URL pattern are returned
            # wrapped with the login_required decorator
            for url in self.required:
                if url.match(request.path):
                    return login_required(view_func)(request, *view_args, **view_kwargs)
    
            # Explicitly return None for all non-matching requests
            return None
    

    그런 다음 settings.py에서 보호하려는 기본 URL을 나열하십시오.

    LOGIN_REQUIRED_URLS = (
        r'/private_stuff/(.*)$',
        r'/login_required/(.*)$',
    )
    

    사이트가 인증이 필요한 페이지에 대한 URL 규칙을 따르는 한이 모델이 작동합니다. 이것이 일대일로 적합하지 않은 경우 미들웨어를 수정하여 상황에 더 밀접하게 맞출 수 있습니다.

    @login_required 데코레이터로 코드베이스를 버려야 할 필요성을 없애는 것 외에도이 접근 방식에 대해 좋아하는 점은 인증 스키마가 변경되면 전역 변경을 수행 할 수있는 한 곳이 있다는 것입니다.

  2. ==============================

    2.각보기 기능에 데코레이터를 배치하는 대신에 다른 방법이 있습니다. urls.py 파일에 login_required () 데코레이터를 넣을 수도 있습니다. 이 작업은 여전히 ​​수동 작업이지만 적어도 한 곳에서 모든 작업을 수행 할 수 있으므로 감사가 쉬워집니다.

    각보기 기능에 데코레이터를 배치하는 대신에 다른 방법이 있습니다. urls.py 파일에 login_required () 데코레이터를 넣을 수도 있습니다. 이 작업은 여전히 ​​수동 작업이지만 적어도 한 곳에서 모든 작업을 수행 할 수 있으므로 감사가 쉬워집니다.

    예를 들어,

        from my_views import home_view
    
        urlpatterns = patterns('',
            # "Home":
            (r'^$', login_required(home_view), dict(template_name='my_site/home.html', items_per_page=20)),
        )
    

    뷰 함수는 문자열이 아니라 직접 이름이 지정되고 가져 오기됩니다.

    또한이 클래스는 클래스를 포함하여 호출 가능한 뷰 객체와 함께 작동합니다.

  3. ==============================

    3.URL을 함수를보기 위해 전달하는 방식을 변경하지 않고 Django에 내장 된 가정을 변경하는 것은 어렵습니다.

    URL을 함수를보기 위해 전달하는 방식을 변경하지 않고 Django에 내장 된 가정을 변경하는 것은 어렵습니다.

    Django 내부에서의 작업 대신, 사용할 수있는 감사가 있습니다. 각보기 기능을 확인하기 만하면됩니다.

    import os
    import re
    
    def view_modules( root ):
        for path, dirs, files in os.walk( root ):
            for d in dirs[:]:
                if d.startswith("."):
                    dirs.remove(d)
            for f in files:
                name, ext = os.path.splitext(f)
                if ext == ".py":
                    if name == "views":
                        yield os.path.join( path, f )
    
    def def_lines( root ):
        def_pat= re.compile( "\n(\S.*)\n+(^def\s+.*:$)", re.MULTILINE )
        for v in view_modules( root ):
            with open(v,"r") as source:
                text= source.read()
                for p in def_pat.findall( text ):
                    yield p
    
    def report( root ):
        for decorator, definition in def_lines( root ):
            print decorator, definition
    

    이것을 실행하고 적절한 데코레이터없이 def의 출력을 검사하십시오.

  4. ==============================

    4.Ber의 대답에서 영감을 얻은 필자는 모든 콜백을 login_required 데코레이터로 래핑하여 패턴 함수를 대신하는 간단한 스 니펫을 작성했습니다. 이것은 장고 1.6에서 작동합니다.

    Ber의 대답에서 영감을 얻은 필자는 모든 콜백을 login_required 데코레이터로 래핑하여 패턴 함수를 대신하는 간단한 스 니펫을 작성했습니다. 이것은 장고 1.6에서 작동합니다.

    def login_required_patterns(*args, **kw):
        for pattern in patterns(*args, **kw):
            # This is a property that should return a callable, even if a string view name is given.
            callback = pattern.callback
    
            # No property setter is provided, so this will have to do.
            pattern._callback = login_required(callback)
    
            yield pattern
    

    그것을 사용하면 다음과 같이 작동합니다 (수확량 때문에 목록에 대한 호출이 필요합니다).

    urlpatterns = list(login_required_patterns('', url(r'^$', home_view)))
    
  5. ==============================

    5.미들웨어는 장고 1.10+에서 새로운 방식으로 작성되어야합니다.

    미들웨어는 장고 1.10+에서 새로운 방식으로 작성되어야합니다.

    import re
    
    from django.conf import settings
    from django.contrib.auth.decorators import login_required
    
    
    class RequireLoginMiddleware(object):
    
        def __init__(self, get_response):
             # One-time configuration and initialization.
            self.get_response = get_response
    
            self.required = tuple(re.compile(url)
                                  for url in settings.LOGIN_REQUIRED_URLS)
            self.exceptions = tuple(re.compile(url)
                                    for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)
    
        def __call__(self, request):
    
            response = self.get_response(request)
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
    
            # No need to process URLs if user already logged in
            if request.user.is_authenticated:
                return None
    
            # An exception match should immediately return None
            for url in self.exceptions:
                if url.match(request.path):
                    return None
    
            # Requests matching a restricted URL pattern are returned
            # wrapped with the login_required decorator
            for url in self.required:
                if url.match(request.path):
                    return login_required(view_func)(request, *view_args, **view_kwargs)
    
            # Explicitly return None for all non-matching requests
            return None
    
    LOGIN_REQUIRED_URLS = (
        r'(.*)',
    )
    LOGIN_REQUIRED_URLS_EXCEPTIONS = (
        r'/admin(.*)$',
    )
    LOGIN_URL = '/admin'
    
  6. ==============================

    6.너는 이길 수 없다. 인증 요구 사항을 선언하기 만하면됩니다. 뷰 기능을 제외하고는이 선언을 어디에 둘 것인가?

    너는 이길 수 없다. 인증 요구 사항을 선언하기 만하면됩니다. 뷰 기능을 제외하고는이 선언을 어디에 둘 것인가?

    보기 기능을 호출 대상으로 대체하는 것이 좋습니다.

    class LoginViewFunction( object ):
        def __call__( self, request, *args, **kw ):
            p1 = self.login( request, *args, **kw )
            if p1 is not None:
                return p1
            return self.view( request, *args, **kw )
        def login( self, request )
            if not request.user.is_authenticated():
                return HttpResponseRedirect('/login/?next=%s' % request.path)
        def view( self, request, *args, **kw ):
            raise NotImplementedError
    

    그런 다음 LoginViewFunction의 뷰 기능 서브 클래스를 작성하십시오.

    class MyRealView( LoginViewFunction ):
        def view( self, request, *args, **kw ):
            .... the real work ...
    
    my_real_view = MyRealView()  
    

    코드 줄을 저장하지 않습니다. 그리고 그것은 "우리가 잊은"문제를 돕지 않습니다. 당신이 할 수있는 일은 코드를 검사하여 뷰 기능이 객체인지 확인하는 것입니다. 올바른 반에서.

    그러나 그때조차도 모든 뷰 기능이 단위 테스트 스위트 없이는 정확하다는 것을 결코 알 수 없을 것입니다.

  7. ==============================

    7.include의 일종으로 모든 URL에 대한 단일 시작 지점을 가질 수 있으며이 패키지 https://github.com/vorujack/decorate_url을 사용하여 꾸미십시오.

    include의 일종으로 모든 URL에 대한 단일 시작 지점을 가질 수 있으며이 패키지 https://github.com/vorujack/decorate_url을 사용하여 꾸미십시오.

  8. ==============================

    8.Django 2.1에서는 클래스의 모든 메소드를 다음과 같이 꾸밀 수 있습니다.

    Django 2.1에서는 클래스의 모든 메소드를 다음과 같이 꾸밀 수 있습니다.

    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    from django.views.generic import TemplateView
    
    @method_decorator(login_required, name='dispatch')
    class ProtectedView(TemplateView):
        template_name = 'secret.html'
    
  9. from https://stackoverflow.com/questions/2164069/best-way-to-make-djangos-login-required-the-default by cc-by-sa and MIT license