Как использовать необходимые разрешения декораторов на основе классов 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)использовать доступ к миксинам, как Джанго.ВНО.автор.миксины.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'просто убедитесь, что ваш миксин первый.
обновление: я опубликовал это еще в 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-вызовом. Просто применяя функцию патча в качестве декоратора, этот декоратор все готово для работы в классах на основе представлений, а также
для тех из вас, кто использует Django > = 1.9, он уже включен в
django.contrib.auth.mixinsкак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: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сторонние представления, которые вы не хотите обертывать, могут быть сделаны в настройках:
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
Comments