Что противоположно декоратору @login_required для представлений Django?

Если я хочу убедиться, что представление указано как имеющее общедоступный доступ, существует ли декоратор, эквивалентный @public_access, который был бы противоположен @login_required и ясно дал бы понять, что представление всегда должно быть общедоступным?

Один вариант использования, который я имею в виду, — это автоматическое добавление «@csrf_exempt» ко всем общедоступным представлениям в дополнение к четкому указанию в коде, что представление должно быть общедоступным.


person MikeN    schedule 12.02.2010    source источник


Ответы (7)


К сожалению, в настоящее время в Django нет встроенной поддержки для этого, что подвергает вас риску раскрытия конфиденциальной информации, когда @login_required случайно забыто.

Вот решение из одного из моих проектов:

middleware/security.py:

def public(function):
    """Decorator for public views that do not require authentication
    """
    orig_func = function
    while isinstance(orig_func, partial):  # if partial - use original function for authorization
        orig_func = orig_func.func
    orig_func.is_public_view = True

    return function

def is_public(function):
    try:                                    # cache is found
        return function.is_public_view
    except AttributeError:                  # cache is not found
        result = function.__module__.startswith('django.') and not function.__module__.startswith('django.views.generic') # Avoid modifying admin and other built-in views

        try:                                # try to recreate cache
            function.is_public_view = result
        except AttributeError:
            pass

        return result


class NonpublicMiddleware(object):

    def process_view_check_logged(self, request, view_func, view_args, view_kwargs):
        return

    def process_view(self, request, view_func, view_args, view_kwargs):
        while isinstance(view_func, partial):  # if partial - use original function for authorization
            view_func = view_func.func

        request.public = is_public(view_func)
        if not is_public(view_func):
            if request.user.is_authenticated():     # only extended checks are needed
                return self.process_view_check_logged(request, view_func, view_args, view_kwargs)

            return self.redirect_to_login(request.get_full_path())  # => login page

    def redirect_to_login(self, original_target, login_url=settings.LOGIN_URL):
        return HttpResponseRedirect("%s?%s=%s" % (login_url, REDIRECT_FIELD_NAME, urlquote(original_target)))

settings.py:

MIDDLEWARE_CLASSES = (
    #...
    'middleware.security.NonpublicProfilefullMiddleware',
    #...
)

и, наконец, просмотреть код:

from <projname>.middleware import publi

@public
def some_view(request):
    #...

# Login required is added automatically
def some_private_view(request):
    #...

Кроме того, вы можете посмотреть "Автоматическое оформление всех представлений проекта django" сообщение в блоге

person Alexander Lebedev    schedule 12.02.2010
comment
Я думаю, что в некоторых случаях также полезно ограничить доступ к определенным функциям, если пользователь вошел в систему. Например, не позволять вошедшему пользователю заполнять регистрационную форму... В В этом случае вы можете использовать комбинацию is_authenticated и is_anonymous: docs.djangoproject.com/en/dev/topics/auth/ - person ; 21.06.2011

Как упоминалось в предыдущем постере, вход в систему не требуется по умолчанию.

Однако иногда бывает полезно заблокировать определенные представления от вошедших в систему пользователей — например, для вошедшего в систему пользователя нет смысла использовать страницу регистрации сайта. В этом случае вы можете сделать что-то подобное на основе существующего декоратора login_required.

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
person LS55321    schedule 15.08.2011
comment
Используйте u.is_anonymous() вместо u.is_authenticated(). См. docs.djangoproject.com/ en/dev/ref/contrib/auth/ для получения дополнительной информации. - person Krozark; 31.10.2013

Немного поздно, но еще один простой способ решить эту проблему — положиться на другой декоратор и добавить лямбда-функцию:

from django.contrib.auth.decorators import user_passes_test

@user_passes_test(lambda u: u.is_anonymous)
person Clément Petitot    schedule 05.05.2017
comment
Какого черта: правки должны быть не менее 6 символов; есть что еще улучшить в этом посте? Конечно нет! В u.is_anonymous отсутствуют только скобки, поэтому пример неверен. - person simplylizz; 13.08.2017
comment
Пример правильный и работает. Где вы хотели поставить дополнительные скобки? - person Clément Petitot; 15.08.2017
comment
О, я проверил документы. Вы правы, это правильно, так как django 1.10+ is_anonymous стал атрибутом. Но в предыдущих версиях Django он всегда давал вам True, потому что is_anonymous был методом. - person simplylizz; 16.08.2017

«Вход в систему не требуется» — значение по умолчанию. Если вы хотите отметить, что представление никогда не должно ограничиваться входом в систему, вы должны сделать это в строке документации.

person Ignacio Vazquez-Abrams    schedule 12.02.2010

Я использую django-decorator-include, чтобы использовать декоратор login_required для защиты всего приложения. , а не по одному просмотру за раз. Основной urls.py моего приложения выглядит так:

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

Это прекрасно работает, за исключением случаев, когда одно из моих приложений имеет одно представление, которое должно быть общедоступным.

Чтобы обойти это, я написал свои собственные login_required и login_not_required. Мой login_required основан на django.contrib.auth.decorators.login_required django, но немного изменен, чтобы действительно учитывать, когда представление помечено как не требующее входа в систему.

Мой проект называется mysite.

Мое приложение называется my_secret_app.

Мое публичное представление в my_secret_app называется MyPublicView.

Все мое решение выглядит так:

мой сайт/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

мой сайт/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):
        ...

Вы должны иметь возможность делать то же самое, независимо от того, являетесь ли вы подклассом View, ListView, CreateView, UpdateView, TemplateView или любым другим. Это также должно работать, если вы используете функцию в качестве своего представления, хотя я не пробовал:

@login_not_required
def MyPublicView(request):
    ...
person John    schedule 20.01.2020

Вы можете использовать user_passes_test и добавить требование, анонимное_требуется. Это будет работать как противоположность login_required, и вы можете использовать его на своей странице регистрации и входа в систему — пользователей немного раздражает то, что они все еще видят страницу входа после входа в систему. Вот как вы это сделаете:

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
person matshidis    schedule 15.06.2021

@permission_classes([разрешения.AllowAny])

person korku02    schedule 28.12.2017
comment
Никаких объяснений - только код. Я начал копать, как импортировать классы разрешений, и оказалось, что это часть rest_framework, которую я не использую. Либо я не прав, и несколько слов о том, откуда его импортировать, и куда добавить этот код помогли бы, либо я прав, и это актуально только в тех случаях, когда кто-то использует rest framework, который там нигде не упоминается в исходном вопросе - person John; 20.01.2020