xtlc
xtlc

Reputation: 1378

Django DRF: Restricting PUT options in ViewSet

I want to restrict my ViewSet so that it allows for different inputs based on permissions:

My models:

class Link(models.Model):
    name = models.CharField("name", max_length = 128)
    page = models.PositiveIntegerField("page", default = 1, choices = [(1, 1), (2, 2), (3, 3)])
    layouts = models.ManyToManyField(Layout)

class Layout(models.Model):
    name = models.CharField("name", max_length = 32) 

Serializer:

class LinkSerializer(serializers.ModelSerializer):
    test = serializers.SerializerMethodField("get_layouts")

    def get_layouts(self): ## this is not displayed?
        return "test" 

    class Meta:
        model = Link
        fields = ["name", "page", "test"]

I have a helper class that provides a all_pages permission for example. in the restframework API I now have a dropdown from page 1-3, but if the user does not have the right to use all pages, I want to only show page 1 for selection (or make it a text field witout input field). Same problem applies for the ManyToMany field: How can I restrict the layouts dropdown options to the ones I selected in the admin-planel for that user?

Right now I manually check everything in the PUT method of the ViewSet and return a "user is not allowed ... -> BAD REQUEST".

EDIT: BC I was asked, right now I do something like this:

class TestViewSet(viewsets.ViewSet):
    serializer_class = LinkSerializer
    http_method_names = ['get', 'put', 'head']
    authentication_classes = [SessionAuthentication,]
    permission_classes = [IsAuthenticated,]

    def list(self, request):
    ...

    def put(self, request):
        ## page logic
        if "app.all_pages" in request.user.get_user_permissions():
            try:
                if not 1 < int(request.data["page"]) <= 3:
                    return Response(data = "page not 2 or 3", 
                                    status = status.HTTP_406_NOT_ACCEPTABLE)
            except Exception as E:
                return Response(data = f"""Error in page selection: {E}""", 
                                status = status.HTTP_400_BAD_REQUEST)
            page = int(request.data["page"])
        else:
            page = 2
        
        ## layout logic:
        store = Store.objects.get(user = request.user)
        allowed_layouts = [l.id for l in store.layouts.all()]
        if not int(request.data["layout"]) in allowed_layouts:
            return Response(data = f"""layout not allowed for store {store}""", 
                            status = status.HTTP_406_NOT_ACCEPTABLE)
        layout = Layout.objects.get(pk = request.data["layout"])
        
        ## do something with page 2 and layout ... in DB/another API   

Upvotes: 0

Views: 458

Answers (1)

Ahmed I. Elsayed
Ahmed I. Elsayed

Reputation: 2130

The question is a bit unclear, so I'm going to explain how permissions work and you'll use them yourself.

P.1

Permissions in DRF as just wrappers for 2 methods, here's how it works.

class PermissionX:
    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj)
        return True

This is a permission, has_permission says (request.user has permission to access the current view?) and has_object_permission says (request.user has persmission to act on obj).

The obj is the object being acted on inside the view. If your view works with users, the object will be an instance of User.

This is used with permission_classes like this

    permission_classes = [IsAuthenticated, PermissionX]

P.2

You usually use user.has_perm(perm) instead of 'name' in user.permissions. This is because the permissions can exist in user.groups and not only user.permissions.

P.3

Any exception raised should be of type PermissionDenied which exists in DRF.

P.4

Extract your permission in a permission.py folder in your current app.

P.5

Your permissions won't be integrated in django admin's interface. This is because it's not using DRF and it's not using your endpoints. It's using things that django made available for it. This is normal, the logic we're talking about will be enforced by your API itself and when you create a custom dashboard, you need to enforce it on the frontend as a type of UX as well.

Upvotes: 1

Related Questions