'What is the opposite of @login_required decorator for Django views?

If I want to make sure that a view is listed as having public access, is there a decorator equivalent to @public_access which would be the opposite of @login_required and make it clear that the view should be publicly accessible always?

One use case I have in mind is to automatically add "@csrf_exempt" to all public views in addition to making it clear in the code that the view should be publicly accessible.



Solution 1:[1]

As a previous poster mentioned, login not required is the default.

However, sometimes it's useful to block certain views from logged in users -- for instance, it makes no sense for a logged-in user to be able to use the site's signup page. In that case, you could do something like this, based off the existing login_required decorator

from django.contrib.auth.decorators import user_passes_test
from django.conf import settings

LOGGED_IN_HOME = settings.LOGGED_IN_HOME

def login_forbidden(function=None, redirect_field_name=None, redirect_to=LOGGED_IN_HOME):
    """
    Decorator for views that checks that the user is NOT logged in, redirecting
    to the homepage if necessary.
    """
    actual_decorator = user_passes_test(
        lambda u: not u.is_authenticated(),
        login_url=redirect_to,
        redirect_field_name=redirect_field_name
    )
    if function:
        return actual_decorator(function)
    return actual_decorator

Solution 2:[2]

A bit late, but another simple way to tackle this issue would be to rely on another decorator and add a lambda function:

from django.contrib.auth.decorators import user_passes_test

@user_passes_test(lambda u: u.is_anonymous)

Solution 3:[3]

You can use user_passes_test and add a requirement, anonymous_required. This would work like the opposite to login_required and you can use on your register and login page - it is a bit irritating for users to still see the login page, after the are logged in. This is how you would do it:

from django.contrib.auth.decorators import user_passes_test

#Anonymous required 
def anonymous_required(function=None, redirect_url=None):

   if not redirect_url:
       redirect_url = settings.LOGIN_REDIRECT_URL

   actual_decorator = user_passes_test(
       lambda u: u.is_anonymous,
       login_url=redirect_url
   )

   if function:
       return actual_decorator(function)
   return actual_decorator


#Application of the Decorator

@anonymous_required
def register(request):
    #Your code goes here

Solution 4:[4]

"Login not required" is the default. If you want to annotate that a view should never be login-restricted then you should do so in the docstring.

Solution 5:[5]

I use django-decorator-include to use the login_required decorator to secure an entire app, rather than one view at a time. My app's main urls.py looks like this:

path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),

This works great, except for when one of my apps has one view which needs to be public.

To get around this, I wrote my own login_required and login_not_required. My login_required is based on django's django.contrib.auth.decorators.login_required, but is slightly modified to actually care when a view is marked as not requiring login.

My project is called mysite.

My app is called my_secret_app.

My public view within my_secret_app is called MyPublicView.

My entire solution looks like this:

mysite/lib.py

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test

# A copy of django.contrib.auth.decorators.login_required that looks for login_not_required attr
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
    actual_decorator = user_passes_test(
        lambda u: u.is_authenticated,
        login_url=login_url,
        redirect_field_name=redirect_field_name
    )

    if function:
        login_req = getattr(function, "login_required", True)

        if login_req:
            return actual_decorator(function)
        else:
            return function
    else:
        return actual_decorator

# Decorator to mark a view as not requiring login to access
def login_not_required(f):
    f.login_required = False
    return f

mysite/urls.py

from .lib import login_required
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),

my_secret_app/views.py:

from django.utils.decorators import method_decorator
from mysite.lib import login_not_required

class MyPublicView(View):
    @method_decorator(login_not_required)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        ...

    def post(self, request, *args, **kwargs):
        ...

You should be able to do the same thing no matter if you're subclassing View, ListView, CreateView, UpdateView, TemplateView, or any of the other ones. It should also work if you're using a function as your view, though I haven't tried it:

@login_not_required
def MyPublicView(request):
    ...

Solution 6:[6]

@permission_classes([permissions.AllowAny])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 LS55321
Solution 2 Clément Petitot
Solution 3
Solution 4 Ignacio Vazquez-Abrams
Solution 5 John
Solution 6 korku02