bcollins
bcollins

Reputation: 3459

Basic Django REST framework - Write only resource

I am trying to create a simple service which allows anonymous users to submit their name and email. I want to AllowAny on adding their info, and IsAuthenticated on everything else. I'm having trouble getting this granularity.

models.py

from django.db import models

class Invitee(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField(max_length=70,blank=True)
    modified = models.DateTimeField(auto_now=True)

serializers.py

class InviteeSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Invitee
        fields = ('name', 'email')

    def create(self, validated_data):
        return Invitee(**validated_data)

views.py

class InviteeViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Invitee.objects.all()
    serializer_class = InviteeSerializer

What and where should I put to make it so users can submit their name and email, but only admins can read, update, delete? Thanks any help.

Upvotes: 1

Views: 2525

Answers (1)

Kevin Brown-Silva
Kevin Brown-Silva

Reputation: 41719

The easiest and safest way to do this is with multiple serializers, one of each user class you need. You will also need to use custom permissions to enforce the read/write difference between authenticated and anonymous users.

class InviteeSerializer(serializers.HyperlinkedModelSerializer):

    def create(self, validated_data):
        return Invitee(**validated_data)

class LimitedInviteeSerializer(InviteeSerializer):

    class Meta:
        model = Invitee
        fields = ('name', 'email', ) # a limited subset of the fields

class FullInviteeSerializer(InviteeSerializer):

    class Meta:
        model = Invitee
        fields = ('name', 'email', "modified", ) # all of the fields

While right now it only looks like you need read/write, if you need full read/write/delete permissions I would recommend reading this Stack Overflow question.

You will also need to control what serializer is being used on the view level. This needs to be done using a combination of permissions and overridden view methods.

class InviteeViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Invitee.objects.all()
    serializer_class = LimitedInviteeSerializer

    def get_serializer_class(self):
        if self.request.user.is_active:
            return FullInviteeSerializer

        return super(InviteeViewSet, self).get_serializer_class()

On the view you will need to override get_serializer_class to determine what serializer should be used based on if the user is active. Anonymous users should never be marked as active, so this is the best way to check while excluding deactivated accounts.

You will also need to create a custom permissions class that will do the opposite of the built-in IsAuthenticatedOrReadOnly permission. You are looking for authenticated users to do everything, and anonymous users to only be write-only. I've called this class IsAuthenticatedOrWriteOnly to match the other permission class.

class IsAuthenticatedOrWriteOnly(BasePermission):
    """
    The request is authenticated as a user, or is a write-only request.
    """

    def has_permission(self, request, view):
        WRITE_METHODS = ["POST", ]

        return (
            request.method in WRITE_METHODS or
            request.user and
            request.user.is_authenticated()
        )

You just need to add this your existing list of permission classes, or override it manually it on the view using permission_classes.

Upvotes: 5

Related Questions