2013-10-11 16 views
14

Ich bin ein Neuling in der Entwicklung mit Django + Django Rest-Framework und ich arbeite an einem Projekt, das REST Api-Zugang bietet. Ich habe mich gefragt, was die beste Vorgehensweise ist, um jeder Aktion eines bestimmten ApiView oder Viewsets eine andere Berechtigung zuzuweisen.Django Rest-Framework pro Aktion Erlaubnis

Angenommen, ich habe einige Berechtigungsklassen definiert wie 'IsAdmin', 'IsRole1', 'IsRole2', ..., und ich möchte den einzelnen Aktionen unterschiedliche Berechtigungen erteilen (z. B. kann ein Benutzer mit Role1 erstellen oder abrufen) , ein Benutzer mit Role2 kann aktualisieren, und nur ein Admin kann löschen).

Wie kann ich eine Klasse basierte Ansicht, um Struktur, die eine Berechtigungsklasse zu den ‚erstellen‘, ‚Liste‘, ‚abrufen‘, ‚Update‘, ‚löschen‘ Aktionen zuweisen? Ich versuche, eine Klasse zu erstellen, die für verschiedene Tabellen mit demselben Berechtigungsmuster wiederverwendet werden kann.

Vielleicht ertrinke ich gerade in einem Zoll Wasser, danke für Ihre Antworten.

Antwort

12

Sie können BasePermission eine custom permission class erstreckt DRF erstellen.

Sie implementieren has_permission, wo Sie Zugriff auf die Objekte request und view haben. Sie können request.user für die entsprechende Rolle überprüfen und True/False entsprechend zurückgeben.

Werfen Sie einen Blick auf die vorgesehenen IsAuthenticatedOrReadOnly Klasse (und anderen) für ein gutes Beispiel dafür, wie einfach es ist.

Ich hoffe, dass hilft.

2

RestFramework der klassenbasierten Ansichten haben Methoden für jede HTTP-Verb (zB: HTTP GET => view.get() usw.). Sie müssen nur django.contrib.auth Berechtigungen, Benutzer, Gruppen und Dekoratoren wie dokumentiert verwenden.

+0

django.contrib.auth den Dekorateure sind nicht immer nützlich, wenn DRF generische Ansichten verwenden. Ziemlich oft implementieren Sie die HTTP-Methoden überhaupt nicht - es gibt also nichts zu dekorieren. (Und sie zu implementieren oder zu versenden, nur um sie zu dekorieren, macht keinen Spaß.) In diesem Fall ist es besser, DRFs eigenes Berechtigungssystem zu verwenden. http://django-rest-framework.org/api-guide/permissions.html –

3

Django hat eine persmissions Klasse genannt DjangoObjectPermissions die Django Wächter als Authentifizierungs-Backend verwendet.

Wenn Django Guardian in Ihren Einstellungen aktiv ist, fügen Sie einfach permission_classes = [DjandoObjectPermissions] zu Ihrer Ansicht hinzu und es wird automatisch Berechtigungsauthentifizierung durchgeführt. Sie können also 'CRUD' basierend auf der Berechtigung auf eine bestimmte django.contrib.auth Gruppe oder Benutzer setzen.

Siehe gist mit einem Beispiel.

Sie Django Wächter einstellen können als Authentifizierungs http://django-guardian.readthedocs.org/en/latest/installation.html

24

In DRF Dokumentation gesichert,

Hinweis: Die Instanzebene has_object_permission Methode wird nur, wenn die has_permission prüft Ansichtsebene aufgerufen werden, sind bereits vergangen

Lasst uns annehmen Erlaubnis folgendes über user Objekt

  • Liste: Mitarbeiter nur
  • erstellen: jemand
  • abrufen: selbst oder Mitarbeiter
  • aktualisieren, Partial Update: selbst oder Mitarbeiter
  • zerstören: Personal nur

permissons.py

from rest_framework import permissions                


class UserPermission(permissions.BasePermission): 

    def has_permission(self, request, view):               
     if view.action == 'list':                 
      return request.user.is_authenticated() and request.user.is_admin       
     elif view.action == 'create':                
      return True                    
     elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:      
      return True                    
     else:                      
      return False                    

    def has_object_permission(self, request, view, obj):            
     if view.action == 'retrieve':                
      return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)  
     elif view.action in ['update', 'partial_update']:           
      return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)  
     elif view.action == 'destroy': 
      return request.user.is_authenticated() and request.user.is_admin       
     else: 
      return False 

views.py

from .models import User 
from .permissions import UserPermission 
from .serializers import UserSerializer 
from rest_framework import viewsets 


class UserViewSet(viewsets.ModelViewSet): 
    queryset = User.objects.all() 
    serializer_class = UserSerializer 
    permission_classes = (UserPermission,) 
+2

'view.action' scheint so viel intuitiver als' request.method'. +1 – PritishC

1

Ich persönlich hasse diese Art von frankenmonster benutzerdefinierten Berechtigungen, meiner Meinung nach, ist es nicht sehr idiomatisch ist, wenn es um Django Framework kommt; Also kam ich auf die folgende Lösung - es ist sehr ähnlich wie @list_route und @detail_route Dekoratoren arbeiten. Wir verlassen uns auf die Tatsache, dass die Methoden/Funktionen sind erstklassige Objekte

Als erstes habe ich solche dectorator bin erstellen:

decorators.py

def route_action_arguments(**kwargs): 
""" 
Add arguments to the action method 
""" 
def decorator(func): 
    func.route_action_kwargs = kwargs 
    return func 
return decorator 

Wie Sie sehen können es fügt ein Wörterbuch der Funktion hinzu, die es mit Parametern schmückt, die als arg-Liste

überreicht werden. Jetzt schuf ich solch ein mixin: mixins. py

class RouteActionArgumentsMixin (object): 
    """ 
    Use action specific parameters to 
    provide: 
    - serializer 
    - permissions 
    """ 

def _get_kwargs(self): 
    action = getattr(self, 'action') 
    if not action: 
     raise AttributeError 
    print('getting route kwargs for action:' + action) 
    action_method = getattr(self, action) 
    kwargs = getattr(action_method, 'route_action_kwargs') 
    print(dir(kwargs)) 
    return kwargs 

def get_serializer_class(self): 
    try: 
     kwargs = self._get_kwargs() 
     return kwargs['serializer'] 
    except (KeyError, AttributeError): 
     return super(RouteActionArgumentsMixin, self).get_serializer_class() 

def get_permissions(self): 
    try: 
     kwargs = self._get_kwargs() 
     return kwargs['permission_classes'] 
    except (KeyError, AttributeError): 
     return super(RouteActionArgumentsMixin, self).get_permissions() 

Mixin macht zwei Dinge; wenn get_permissions aufgerufen wird, überprüft er die ‚Aktion‘ ausgeführt wird, und die looksup permission_classes Sammlung aus der mit den viewset.action_method.route_action_kwargs ‚route_action_kwargs‘ zugeordnet

wenn get_serializer_class genannt wird, tut es das gleiche und nimmt die " Serializer‘von‚

Now‘route_action_kwargs die Art und Weise können wir es verwenden:

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create') 
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet): 
    """ 
    User and profile managment viewset 
    """ 

    queryset = User.objects.all() 
    serializer_class = UserSerializer 

    @list_route(methods=['post']) 
    @route_action_arguments(permission_classes=(AllowAny,), serializer=LoginSerializer) 
    def login(self, request): 
     serializer = self.get_serializer_class()(data=request.data) 

Für benutzerdefinierte routse wir explizit definieren, können wir die @route_action_arguments explizit auf der Methode nur.

Im Hinblick auf den allgemeinen Viewset und Methoden, können wir sie noch hinzufügen, um den @method_decorator mit

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create') 
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):