J'ai un peu de difficulté à comprendre comment fonctionne le nouveau CBV. Ma question est la suivante: il est nécessaire d’exiger une connexion dans toutes les vues et, dans certaines d’entre elles, des autorisations spécifiques. Dans les vues basées sur les fonctions, je le fais avec @permission_required () et l'attribut login_required dans la vue, mais je ne sais pas comment procéder dans les nouvelles vues. Existe-t-il une section dans la documentation Django expliquant cela? Je n'ai rien trouvé. Quel est le problème dans mon code?
J'ai essayé d'utiliser le @method_decorator mais il répond " TypeError à/spaces/prueba/_wrapped_view () prend au moins 1 argument (0 donné) "
Voici le code (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
Quelques stratégies sont énumérées dans les documents CBV :
Ajoutez le décorateur dans votre itinéraire urls.py
, par exemple, login_required(ViewSpaceIndex.as_view(..))
Décorez la méthode dispatch
de votre CBV avec un method_decorator
p. Ex.,
from Django.utils.decorators import method_decorator
@method_decorator(login_required, name='dispatch')
class ViewSpaceIndex(TemplateView):
template_name = 'secret.html'
Avant Django 1.9, vous ne pouvez pas utiliser method_decorator
sur la classe, vous devez donc remplacer la méthode dispatch
:
class ViewSpaceIndex(TemplateView):
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
Utilisez les mixins d’accès tels que Django.contrib.auth.mixins.LoginRequiredMixin disponibles dans Django 1.9+ et bien décrits dans les autres réponses ici:
from Django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
La raison pour laquelle vous obtenez une TypeError
est expliquée dans la documentation:
Remarque: method_decorator transmet * args et ** kwargs en tant que paramètres à la méthode décorée de la classe. Si votre méthode n'accepte pas de jeu de paramètres compatible, une exception TypeError sera générée.
Voici mon approche, je crée un mixin protégé (ceci est conservé dans ma bibliothèque de 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)
Chaque fois que vous voulez qu'une vue soit protégée, ajoutez simplement le mix approprié:
class SomeProtectedViewView(LoginRequiredMixin, TemplateView):
template_name = 'index.html'
Assurez-vous que votre mixin est le premier.
Mise à jour: J'ai déjà posté cela depuis 2011, à partir de la version 1.9. Django inclut maintenant ce mix et d'autres mixins utiles (AccessMixin, PermissionRequiredMixin, UserPassesTestMixin) en standard!
Voici une alternative en utilisant des décorateurs de classe:
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
Ceci peut alors être utilisé simplement comme ceci:
@class_view_decorator(login_required)
class MyView(View):
# this view now decorated
Je me rends compte que ce fil est un peu démodé, mais voici mes deux centimes de toute façon.
avec le code suivant:
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
nous avons maintenant un moyen de réparer un décorateur, de sorte qu'il deviendra multifonctionnel. Cela signifie effectivement que lorsqu'il est appliqué à un décorateur de vue régulier, comme ceci:
login_required = patch_view_decorator(login_required)
ce décorateur fonctionnera quand même comme il était initialement prévu:
@login_required
def foo(request):
return HttpResponse('bar')
mais fonctionnera également correctement lorsqu'il est utilisé de la manière suivante:
@login_required
class FooView(DetailView):
model = Foo
Cela semble bien fonctionner dans plusieurs cas que j'ai récemment rencontrés, y compris cet exemple concret:
@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
La fonction ajax_view est écrite pour modifier une vue (basée sur une fonction), de sorte qu'elle génère une erreur 404 chaque fois que cette vue est visitée par un appel non ajax. En appliquant simplement la fonction de patch en tant que décorateur, ce décorateur est prêt à travailler dans des vues basées sur la classe.
Pour ceux qui utilisent Django> = 1.9, il est déjà inclus dans Django.contrib.auth.mixins
sous la forme AccessMixin
, LoginRequiredMixin
, PermissionRequiredMixin
et UserPassesTestMixin
.
Donc, pour appliquer LoginRequired à CBV (par exemple, 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'
Il est également bon de garder à l’esprit le GCBV Mixin ordre: Mixins doit être placé du côté gauche et la classe vue de base doit être du côté droit. . Si l'ordre est différent, vous pouvez obtenir des résultats brisés et imprévisibles.
Utilisez des bretelles Django. Il fournit beaucoup de mixins utiles et facilement disponibles. Il a de beaux documents. Essaye le.
Vous pouvez même créer vos mixins personnalisés.
http://Django-braces.readthedocs.org/en/v1.4.0/
Exemple de code:
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({})
S'il s'agit d'un site où la majorité des pages nécessitent la connexion de l'utilisateur, vous pouvez utiliser un middleware pour forcer la connexion sur toutes les vues sauf certaines qui sont particulièrement marquées.
Pré-Django 1.10 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
Les vues tierces que vous ne souhaitez pas encapsuler peuvent être déduites des paramètres:
settings.py:
LOGIN_EXEMPT_URL_PREFIXES = ('/login/', '/reset_password/')
Dans mon code, j'ai écrit cet adaptateur pour adapter les fonctions membres à une fonction non membre:
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
Vous pouvez simplement l'utiliser comme ceci:
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')
C'est très facile avec Django> 1.9 et le support de PermissionRequiredMixin
et LoginRequiredMixin
Il suffit d'importer de l'auth
views.py
from Django.contrib.auth.mixins import LoginRequiredMixin
class YourListView(LoginRequiredMixin, Views):
pass
Pour plus de détails, lire Autorisation dans Django
Si vous effectuez un projet nécessitant une variété de tests d'autorisation, vous pouvez hériter de cette classe.
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)
J'ai fait ce correctif basé sur la solution de Josh
class LoginRequiredMixin(object):
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
Exemple d'utilisation:
class EventsListView(LoginRequiredMixin, ListView):
template_name = "events/list_events.html"
model = Event