Как использовать разрешения, необходимые декораторы на представлениях класса django
У меня возникли проблемы с пониманием того, как работают новые CBVs. Мой вопрос в том, что мне нужно требовать входа во все представления, а в некоторых из них-определенных разрешений. В функциональных представлениях я делаю это с помощью @permission_required() и атрибута login_required в представлении, но я не знаю, как это сделать в новых представлениях. Есть ли какой-то раздел в документах django, объясняющий это? Я ничего не нашел. Что не так в моем коде?
Я попытался использовать @method_decorator но он отвечает:"TypeError at /spaces / prueba/ _wrapped_view () принимает не менее 1 аргумента (0 дано)"
вот код (GPL):
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required, permission_required
class ViewSpaceIndex(DetailView):
"""
Show the index page of a space. Get various extra contexts to get the
information for that space.
The get_object method searches in the user 'spaces' field if the current
space is allowed, if not, he is redirected to a 'nor allowed' page.
"""
context_object_name = 'get_place'
template_name = 'spaces/space_index.html'
@method_decorator(login_required)
def get_object(self):
space_name = self.kwargs['space_name']
for i in self.request.user.profile.spaces.all():
if i.url == space_name:
return get_object_or_404(Space, url = space_name)
self.template_name = 'not_allowed.html'
return get_object_or_404(Space, url = space_name)
# Get extra context data
def get_context_data(self, **kwargs):
context = super(ViewSpaceIndex, self).get_context_data(**kwargs)
place = get_object_or_404(Space, url=self.kwargs['space_name'])
context['entities'] = Entity.objects.filter(space=place.id)
context['documents'] = Document.objects.filter(space=place.id)
context['proposals'] = Proposal.objects.filter(space=place.id).order_by('-pub_date')
context['publication'] = Post.objects.filter(post_space=place.id).order_by('-post_pubdate')
return context
11 ответов
есть несколько стратегий, перечисленных в расширения CBV документы:
добавьте декоратора в свой
urls.py
маршрут, например,login_required(ViewSpaceIndex.as_view(..))
-
Украсьте свой CBV в
dispatch
методmethod_decorator
например,from django.utils.decorators import method_decorator @method_decorator(login_required, name='dispatch') class ViewSpaceIndex(TemplateView): template_name = 'secret.html'
перед Django 1.9 вы не можете использовать
method_decorator
в классе, поэтому вы должны переопределитьdispatch
способ:class ViewSpaceIndex(TemplateView): @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
-
использовать доступ к mixins, как Джанго.ВНО.автор.миксины.LoginRequiredMixin доступно в Django 1.9+ и хорошо описано в других ответах здесь:
from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
вы получаете TypeError
объясняется в документах:
Примечание.: method_decorator передает *args и **kwargs в качестве параметров декорированному методу класса. Если ваш метод не принимает совместимый набор параметров, он вызовет TypeError исключение.
вот мой подход, я создаю mixin, который защищен (это хранится в моей библиотеке mixin):
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
class LoginRequiredMixin(object):
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
всякий раз, когда вы хотите, чтобы представление было защищено, просто добавьте соответствующий mixin:
class SomeProtectedViewView(LoginRequiredMixin, TemplateView):
template_name = 'index.html'
просто убедитесь, что ваш mixin является первым.
обновление: я опубликовал это еще в 2011 году, начиная с версии 1.9 Django теперь включает в себя этот и другие полезные миксины (AccessMixin, PermissionRequiredMixin, UserPassesTestMixin) как стандарт!
вот альтернатива с использованием декораторов на основе классов:
from django.utils.decorators import method_decorator
def class_view_decorator(function_decorator):
"""Convert a function based decorator into a class based decorator usable
on class based Views.
Can't subclass the `View` as it breaks inheritance (super in particular),
so we monkey-patch instead.
"""
def simple_decorator(View):
View.dispatch = method_decorator(function_decorator)(View.dispatch)
return View
return simple_decorator
Это можно использовать просто так:
@class_view_decorator(login_required)
class MyView(View):
# this view now decorated
Я понимаю, что эта нить немного устарела, но вот мои два цента в любом случае.
следующий код:
from django.utils.decorators import method_decorator
from inspect import isfunction
class _cbv_decorate(object):
def __init__(self, dec):
self.dec = method_decorator(dec)
def __call__(self, obj):
obj.dispatch = self.dec(obj.dispatch)
return obj
def patch_view_decorator(dec):
def _conditional(view):
if isfunction(view):
return dec(view)
return _cbv_decorate(dec)(view)
return _conditional
теперь у нас есть способ исправить декоратора, поэтому он станет многофункциональным. Это фактически означает, что при применении к обычному декоратору вида, например:
login_required = patch_view_decorator(login_required)
этот декоратор по-прежнему будет работать при использовании так, как он был первоначально предназначен:
@login_required
def foo(request):
return HttpResponse('bar')
но также будет работать правильно при использовании как Итак:
@login_required
class FooView(DetailView):
model = Foo
кажется, это работает нормально в нескольких случаях, с которыми я недавно столкнулся, включая этот реальный пример:
@patch_view_decorator
def ajax_view(view):
def _inner(request, *args, **kwargs):
if request.is_ajax():
return view(request, *args, **kwargs)
else:
raise Http404
return _inner
функция ajax_view записывается для изменения представления (основанного на функции), так что она вызывает ошибку 404 всякий раз, когда это представление посещается вызовом не ajax. Просто применяя функцию патча в качестве декоратора, этот декоратор также настроен на работу в представлениях на основе классов
для тех из вас, кто использует Джанго >= 1.9, он уже включен в django.contrib.auth.mixins
as AccessMixin
, LoginRequiredMixin
, PermissionRequiredMixin
и UserPassesTestMixin
.
Итак, чтобы применить LoginRequired к CBV (например,DetailView
):
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.detail import DetailView
class ViewSpaceIndex(LoginRequiredMixin, DetailView):
model = Space
template_name = 'spaces/space_index.html'
login_url = '/login/'
redirect_field_name = 'redirect_to'
также хорошо иметь в виду порядок Gcbv Mixin:Mixins должен идти на левый сторону, а вид класс должен идти в право стороне. Если порядок отличается, Вы можете получить сломанные и непредсказуемые результаты.
Если это сайт, где большинство страниц требует, чтобы пользователь вошел в систему, вы можете использовать промежуточное ПО для принудительного входа во все представления за исключением некоторые, особо отмечены.
middleware.py:
from django.contrib.auth.decorators import login_required
from django.conf import settings
EXEMPT_URL_PREFIXES = getattr(settings, 'LOGIN_EXEMPT_URL_PREFIXES', ())
class LoginRequiredMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
path = request.path
for exempt_url_prefix in EXEMPT_URL_PREFIXES:
if path.startswith(exempt_url_prefix):
return None
is_login_required = getattr(view_func, 'login_required', True)
if not is_login_required:
return None
return login_required(view_func)(request, *view_args, **view_kwargs)
views.py:
def public(request, *args, **kwargs):
...
public.login_required = False
class PublicView(View):
...
public_view = PublicView.as_view()
public_view.login_required = False
сторонние представления, которые вы не хотите обертывать, можно сделать excempt в настройках:
settings.py:
LOGIN_EXEMPT_URL_PREFIXES = ('/login/', '/reset_password/')
Используйте Фигурные Скобки Django. Он обеспечивает много полезных миксинов, которые легко доступны. У него красивые документы. Попробовать ее.
вы даже можете создать свой собственный миксины.
http://django-braces.readthedocs.org/en/v1.4.0/
Пример Кода:
from django.views.generic import TemplateView
from braces.views import LoginRequiredMixin
class SomeSecretView(LoginRequiredMixin, TemplateView):
template_name = "path/to/template.html"
#optional
login_url = "/signup/"
redirect_field_name = "hollaback"
raise_exception = True
def get(self, request):
return self.render_to_response({})
в моем коде я написал этот адаптер для адаптации функции-члены функции, не являющейся членом:
from functools import wraps
def method_decorator_adaptor(adapt_to, *decorator_args, **decorator_kwargs):
def decorator_outer(func):
@wraps(func)
def decorator(self, *args, **kwargs):
@adapt_to(*decorator_args, **decorator_kwargs)
def adaptor(*args, **kwargs):
return func(self, *args, **kwargs)
return adaptor(*args, **kwargs)
return decorator
return decorator_outer
Вы можете просто использовать его как это:
from django.http import HttpResponse
from django.views.generic import View
from django.contrib.auth.decorators import permission_required
from some.where import method_decorator_adaptor
class MyView(View):
@method_decorator_adaptor(permission_required, 'someapp.somepermission')
def get(self, request):
# <view logic>
return HttpResponse('result')
Это очень легко с django > 1.9 поставляется с поддержкой PermissionRequiredMixin
и LoginRequiredMixin
просто импортируйте из auth
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
class YourListView(LoginRequiredMixin, Views):
pass
более подробная информация авторизация в django
Если вы делаете проект, который требует различных тестов разрешений, вы можете наследовать этот класс.
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from django.views.generic import View
from django.utils.decorators import method_decorator
class UserPassesTest(View):
'''
Abstract base class for all views which require permission check.
'''
requires_login = True
requires_superuser = False
login_url = '/login/'
permission_checker = None
# Pass your custom decorator to the 'permission_checker'
# If you have a custom permission test
@method_decorator(self.get_permission())
def dispatch(self, *args, **kwargs):
return super(UserPassesTest, self).dispatch(*args, **kwargs)
def get_permission(self):
'''
Returns the decorator for permission check
'''
if self.permission_checker:
return self.permission_checker
if requires_superuser and not self.requires_login:
raise RuntimeError((
'You have assigned requires_login as False'
'and requires_superuser as True.'
" Don't do that!"
))
elif requires_login and not requires_superuser:
return login_required(login_url=self.login_url)
elif requires_superuser:
return user_passes_test(lambda u:u.is_superuser,
login_url=self.login_url)
else:
return user_passes_test(lambda u:True)
Я сделал это исправление на основе решения Джоша
class LoginRequiredMixin(object):
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
пример использования:
class EventsListView(LoginRequiredMixin, ListView):
template_name = "events/list_events.html"
model = Event