Django 클래스 뷰 및 Mixin
20870 단어 Django
# django.core.handlers.base.py
#
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
# view
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
except Exception as e:
response = self.process_exception_by_middleware(e, request) 만약 개발자가 함수 보기(FBV)를 사용한다면 이 코드는 매우 이해하기 쉽다. wrapped백은 이 함수 보기입니다. 직접 호출하면 됩니다.
그러나 클래스 보기라면, Django는 어떻게 클래스 보기를 함수로 바꿉니까?
CBV.as_view()
Django의 루트에서 URL에view를 지정할 때, 클래스 보기 (모든 클래스 보기가 기본 보기 계승되어야 함) 를 사용하려면 asview 방법은 클래스 보기를 하나의 함수로 전환시킨다. 다음에 필자는 as 를 분석해 보겠다.view 방법의 원본 코드입니다.
class View(object):
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
for key, value in six.iteritems(kwargs):
setattr(self, key, value)
# as_view ,
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return http.HttpResponseNotAllowed(self._allowed_methods()) 사실 as방법은 방법에서view 함수를 정의했다. 이 함수가 받아들인 매개 변수는 FBV가 받아들인 매개 변수와 같지만 이 함수는 http의method에 따라 구체적인 CBV의method에 분배되는 것이 아니라 디스패치 방법에 맡겨 완성한다.마지막 asview 방법이 이view 함수를 되돌려줍니다.
디스패치 방법에서 개발자가 스스로 정의한 get,post 등 방법에 대응하는 업무 논리를 찾아 실행 결과를 되돌려줍니다. 개발자가 이 방법을 정의하지 않으면 405로 되돌려주는 것은 이 방법이 허용되지 않는다는 것을 의미합니다.
Mixin
FBV에서는 뷰 액세스에 제한을 가하고자 하면 직접 인테리어를 사용할 수 있지만, CBV에서는 직접 인테리어를 사용할 수 없다.
예를 들어django auth에서 제공하는 기본loginrequired 장식기와permissionRequired 장식기는 CBV에서 LoginRequired Mixin과PermissionRequired Mixin이다.
두 Mixin 모두 Access Mixin (이로 인해 발생하는 이상 점프가 같은 URL을 가리킨다) 에 계승되어 있으며, 다음은 그들의 원본 분석을 보겠습니다.
class LoginRequiredMixin(AccessMixin):
"""
CBV mixin which verifies that the current user is authenticated.
"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
# AccessMixin
return self.handle_no_permission()
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
class PermissionRequiredMixin(AccessMixin):
"""
CBV mixin which verifies that the current user has all specified
permissions.
"""
permission_required = None
def get_permission_required(self):
"""
Override this method to override the permission_required attribute.
Must return an iterable.
"""
if self.permission_required is None:
raise ImproperlyConfigured(
'{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
'{0}.get_permission_required().'.format(self.__class__.__name__)
)
if isinstance(self.permission_required, six.string_types):
perms = (self.permission_required, )
else:
perms = self.permission_required
return perms
def has_permission(self):
"""
Override this method to customize the way permissions are checked.
"""
perms = self.get_permission_required()
return self.request.user.has_perms(perms)
def dispatch(self, request, *args, **kwargs):
if not self.has_permission():
# AccessMixin
return self.handle_no_permission()
return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs) 여러분 보시다시피 이 두 Mixin은 모두 디스패치 방법을 실현했고 실행이 끝난 후에 MRO 그룹의 다음 종류에 따라 디스패치 방법을 사용했습니다.(MRO는 파이톤을 다중으로 계승할 때 사용하는 방법입니다. 잘 모르는 학생은 이 글을 참고하세요. 파이톤의 MRO 알고리즘을 정말 이해합니까?)
간단한 예를 들어 필자가 CBV를 사용자 정의했다고 가정해 보자.
class MyView(LoginRequiredMixin, PermissionRequiredMixin, View) 그럼 디스패치가 순서대로 호출되는 순서는LoginRequiredMixin,PermissionRequiredMixn,AccessMixin(disptach 방법 없음),View입니다.따라서 기본 뷰는 다중 계승의 맨 뒤에 두어야 한다. 왜냐하면 디스패치 방법의 호출은 업무 논리를 대표하는handler이기 때문이다.
LoginRequiredMixin이나PermissionRequiredMixin이AccessMixin을 계승하는handleno_permission 방법 시 raiseexception이 True로 설정되어 Permission Denied의 이상을 던집니다. 그렇지 않으면 login 으로 이동합니다.url.
class AccessMixin(object):
"""
Abstract CBV mixin that gives access mixins the same customizable
functionality.
"""
login_url = None
permission_denied_message = ''
raise_exception = False
redirect_field_name = REDIRECT_FIELD_NAME
def get_login_url(self):
"""
Override this method to override the login_url attribute.
"""
login_url = self.login_url or settings.LOGIN_URL
if not login_url:
raise ImproperlyConfigured(
'{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override '
'{0}.get_login_url().'.format(self.__class__.__name__)
)
return force_text(login_url)
def get_permission_denied_message(self):
"""
Override this method to override the permission_denied_message attribute.
"""
return self.permission_denied_message
def get_redirect_field_name(self):
"""
Override this method to override the redirect_field_name attribute.
"""
return self.redirect_field_name
def handle_no_permission(self):
if self.raise_exception:
raise PermissionDenied(self.get_permission_denied_message())
return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name()) 이 이상은 먼저view 함수를 실행하는 코드에 포착됩니다. 즉, 본고의 첫 번째 코드 세그먼트입니다. 그리고self를 호출합니다.process_exception_by_middleware.
def process_exception_by_middleware(self, exception, request):
"""
Pass the exception to the exception middleware. If no middleware
return a response for this exception, raise it.
"""
for middleware_method in self._exception_middleware:
response = middleware_method(request, exception)
if response:
return response
raise 이상에 대한 정의가 없기 때문에 프로그램은 마지막 줄에 있는 raise의 이상을 직접 실행합니다. 그러면 이 이상은 누가 포착했습니까?
필자가 이전 블로그(Django 처리 http 요청 프로세스 분석)에서 언급한convertexception_to_response가 self. 를 장식했어요.get_legacy_response라는 거요? 이 이상은 convert 함수에 포착되었습니다.
def convert_exception_to_response(get_response):
"""
Wrap the given get_response callable in exception-to-response conversion.
All exceptions will be converted. All known 4xx exceptions (Http404,
PermissionDenied, MultiPartParserError, SuspiciousOperation) will be
converted to the appropriate response, and all other exceptions will be
converted to 500 responses.
This decorator is automatically applied to all middleware to ensure that
no middleware leaks an exception and that the next middleware in the stack
can rely on getting a response instead of an exception.
"""
@wraps(get_response, assigned=available_attrs(get_response))
def inner(request):
try:
response = get_response(request)
except Exception as exc:
response = response_for_exception(request, exc)
return response
return inner
def response_for_exception(request, exc):
if isinstance(exc, Http404):
if settings.DEBUG:
response = debug.technical_404_response(request, exc)
else:
response = get_exception_response(request, get_resolver(get_urlconf()), 404, exc)
elif isinstance(exc, PermissionDenied):
logger.warning(
'Forbidden (Permission denied): %s', request.path,
extra={'status_code': 403, 'request': request},
)
response = get_exception_response(request, get_resolver(get_urlconf()), 403, exc)
elif isinstance(exc, MultiPartParserError):
logger.warning(
'Bad request (Unable to parse request body): %s', request.path,
extra={'status_code': 400, 'request': request},
)
response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc)
elif isinstance(exc, SuspiciousOperation):
# The request logger receives events for any problematic request
# The security logger receives events for all SuspiciousOperations
security_logger = logging.getLogger('django.security.%s' % exc.__class__.__name__)
security_logger.error(
force_text(exc),
extra={'status_code': 400, 'request': request},
)
if settings.DEBUG:
response = debug.technical_500_response(request, *sys.exc_info(), status_code=400)
else:
response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc)
elif isinstance(exc, SystemExit):
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
raise
else:
signals.got_request_exception.send(sender=None, request=request)
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
return response 예를 들어PermissionDenied 이상에 대응하는 상태 코드는 403이고 urls에서 대응하는 상태 코드의view 함수를 정의하면 이 함수를 호출합니다.
예를 들면 다음과 같습니다.
# urls.py
handler403 = 'path/to/handler_view' 마지막 상황은 LoginRequiredMixin과PermissionRequiredMixin이 다른login으로 이동하기를 원한다면url 어떡하지?계승할 때 LoginRequiredMixin을 먼저 사용한 다음에 구체적인 http 방법에method 를 추가하는 것을 고려할 수 있다decorator.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Django 라우팅 계층 URLconf 작용 및 원리 해석URL 구성(URLconf)은 Django가 지원하는 웹 사이트의 디렉토리와 같습니다.그것의 본질은 URL과 이 URL을 호출할 보기 함수 사이의 맵표입니다. 위의 예제에서는 URL의 값을 캡처하고 위치 매개 변수로...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.