Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework: Best practices?

I was wondering about best practices when it comes to Django Rest Framework. I've been limiting access to changing certain attributes on an account via the usage of different serializers per user (staff vs. account owner vs. anyone else) and HTTP method, but I'm feeling like this is too unpythonic.

Is this the best way to accomplish my task of separating out "permissions" to change different fields of an object? Or is there a better and more pythonic way to accomplish what I'm doing in this manner currently?

Any and all criticism with the below code is accepted, as I feel like I've cut some corners.

Thanks so much.

from rest_framework import serializers, viewsets
from rest_framework.permissions import SAFE_METHODS
from accounts.models import User
from cpapi.permissions import *


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'url', 'username', 'password')
        write_only_fields = ('password',)

    def restore_object(self, attrs, instance=None):
        user = super(UserSerializer, self).restore_object(attrs, instance)
        if 'password' in attrs.keys():
            user.set_password(attrs['password'])
        return user

class UserDetailsSerializer(UserSerializer):
    class Meta(UserSerializer.Meta):
        fields = ('id', 'url', 'username', 'password', 'email')

class UserListSerializer(UserSerializer):
    class Meta(UserSerializer.Meta):
        fields = ('id', 'url', 'username')

class UserWithoutNameSerializer(UserSerializer):
    class Meta(UserSerializer.Meta):
        fields = ('id', 'url', 'password', 'email')


class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    serializer_class = UserSerializer
    model = User

    def get_serializer_class(self): # Modify to allow different information for different access (userlist vs staff)
        serializer_class = self.serializer_class
        if 'List' in self.get_view_name():
            serializer_class = UserListSerializer
        elif self.request.method in ['PUT', 'PATCH']:
            serializer_class = UserWithoutNameSerializer
        elif self.get_object() == self.request.user or self.request.user.is_staff:
            serializer_class = UserDetailsSerializer
        return serializer_class

    def get_permissions(self):
        if self.request.method in SAFE_METHODS or self.request.method == 'POST':
            return [AllowAny()]
        elif self.request.method == 'DELETE':
            return [IsAdminUser()]
        else:
            return [IsStaffOrTargetUser()]
like image 554
Brad Reardon Avatar asked May 18 '14 23:05

Brad Reardon


1 Answers

If I understand correctly what you want is per-field permissions, and not Django nor Django-REST-Framework supports them directly. You could install a package like Django Fine-Grained Permissions but you'd be stuck with the same solution because there's no way to comunicate those permissions to the REST API view.

I'd recommend that you stick to your solution, or put the serializer selection logic in one serializer that has all the fields, and pass the role to the constructor, and let the serializer build a field list according to it, but for that to work you would need to write your own Serializer subclass.

like image 194
fixmycode Avatar answered Sep 24 '22 09:09

fixmycode