Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is DRF browsable API running permission checks on multiple request types for every actual request?

I have a simple DRF list view and wanted to write some permissions pertaining to POST requests. That resulted in an error when GET requests were issued. That led me to realize that my permission class is being called multiple times on requests that were not submitted. Here are my files.

permissons.py:

class IsDummy(permissions.BasePermission):
    def has_permission(self, request, view):
        print("\n{}\n".format(request.method)) 
        if request.method in permissions.SAFE_METHODS:
            return True
        return False

views.py:

class UserListView(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsDummy]

The issue only happens when I submit a request from my browser on the browsable api. When I submit a GET request at the list url I get the following printed to the terminal from the print statement in the IsDummy permission class:

GET

POST

POST

OPTIONS

When I submit a GET or OPTIONS request through postman I see the single, appropriate, request method that I actually used.

It seems that the first method listed is always the actual method that I used, I have no idea where the extra POSTs and the OPTION are coming from. The even stranger part is that the page will load fine after all of this even though the POST requests should clearly be resulting in IsDummy.has_permission returning a False.

The chrome dev tools show only a single GET request being submitted and since it only seems to happen in the browsable api i'm sure it has something to do with that but I can't figure out what I have messed up to make this happen.

like image 239
c6754 Avatar asked Oct 21 '25 18:10

c6754


1 Answers

Browser API is a single page which can add\update\delete instances. When you ask this page, DRF will check all required permission to see if the corresponding module is allowed to display.

Deep in the DRF source code, you can see this in renderers.py:

class BrowsableAPIRenderer(BaseRenderer):
    """
    HTML renderer used to self-document the API.
    """
    media_type = 'text/html'
    format = 'api'
    template = 'rest_framework/api.html'
    filter_template = 'rest_framework/filters/base.html'
    code_style = 'emacs'
    charset = 'utf-8'
    form_renderer_class = HTMLFormRenderer

    ...

    def get_context(self, data, accepted_media_type, renderer_context):
         ....
         context = {
             ....
            'put_form': self.get_rendered_html_form(data, view, 'PUT', request),
            'post_form': self.get_rendered_html_form(data, view, 'POST', request),
            'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request),
            'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request),
         }
         return context

    def get_rendered_html_form(self, data, view, method, request):
        ....
        if not self.show_form_for_method(view, method, request, instance):
            ....

    def show_form_for_method(self, view, method, request, obj):
        """
        Returns True if a form should be shown for this method.
        """
        if method not in view.allowed_methods:
            return  # Not a valid method

        try:
            view.check_permissions(request)
            if obj is not None:
                view.check_object_permissions(request, obj)
        except exceptions.APIException:
            return False  # Doesn't have permissions
        return True

view.check_permissions(request) in show_form_for_method is why DRF browsable API is running permission checks on multiple request types for every actual request.

like image 186
Ykh Avatar answered Oct 23 '25 10:10

Ykh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!