I am trying to better understand "behind the scenes" of the Django authorization decorators. Although I think I understand decorators in general, I find it difficult to understand the authorization decorators code. Is there any "line by line" explanation of the code (https://docs.djangoproject.com/en/2.2/_modules/django/contrib/auth/decorators/)?
I don't know of anywhere that has a line-by-line documentation of these decorators, but here's my take on it.
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
This function serves as the basis for Django's decorator based authentication. It accepts a test function which will have the user passed to it to determine whether or not that user has access to the decorated view.
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
This chunk of code is just standard Python decorator stuff - if you understand decorators there's not really anything to explain.
if test_func(request.user):
return view_func(request, *args, **kwargs)
Here the test function is called with the user. If the user passes the test the original view function is immediately returned and no further action is taken.
path = request.build_absolute_uri()
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
Retrieve the current request's url and the login url. The login url can be
passed to the user_passes_test
decorator or the default value in the Django
settings can be used.
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
Retrieve the HTTP scheme (http or https) and the netloc (www.example.com plus the port if applicable) from both the current url and the login url.
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
If the HTTP scheme and the netloc for the two urls match then the path is set to the relative url rather than the absolute url.
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
Redirect the request to the login page. redirect_to_login
will send the user
to get logged in with a ?next=
GET parameter equal to the current path.
return _wrapped_view
return decorator
Finishing decorator stuff.
login_required
is just a shortcut for user_passes_test
which already supplies the test_func
- a simple function which checks the value of user.is_authenticated
.
permission_required
Does the same but takes the name of a permission or a list of permission names and checks that the user has those permissions. permission_required
also has the added feature that you can pass raise_exception=True
to raise a 403 instead of redirecting to the login url.