Django user_passes_test usage
<p>I have a function-based view in Django:</p>

<pre><code>@login_required
def bout_log_update(request, pk):
...
</code></pre>

<p>While it's protected from people who aren't logged in, I need to be able to restrict access to this view based on:
1. The user currently logged in
2. Which user created the object (referred to by pk)</p>

<p>It needs to be accessible only if the currently logged in user created the object being accessed, or is a superuser.</p>

<p>Can the standard @user_passes_test decorator accomplish this?  Or a custom decorator?  Or another method entirely?</p>

<p>I'd re-write it as a class-based view and use UserPassesTestMixin if I could, but I don't know that it's possible for this particular view.</p>

<p>You can achieve this quite easily with a custom decorator <a href="https://docs.djangoproject.com/en/1.10/_modules/django/contrib/auth/decorators/#user_passes_test" rel="nofollow">based on <code>user_passes_test</code> source</a>:</p>

<pre><code>def my_user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
    """
    Decorator for views that checks that the user passes the given test,
    redirecting to the log-in page if necessary. The test should be a callable
    that takes the user object and returns True if the user passes.
    """

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            # the following line is the only change with respect to
            # user_passes_test:
            if test_func(request.user, *args, **kwargs):
                return view_func(request, *args, **kwargs)
            path = request.build_absolute_uri()
            resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
            # 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]
            if ((not login_scheme or login_scheme == current_scheme) and
                    (not login_netloc or login_netloc == current_netloc)):
                path = request.get_full_path()
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(
                path, resolved_login_url, redirect_field_name)
        return _wrapped_view
    return decorator
</code></pre>

<p>Note that just one line is changed from <code>test_func(request.user)</code> to <code>test_func(request.user, *args, **kwargs)</code> so that all arguments passed to the view are passed to the test function too.</p>

python django