brocksamson brocksamson - 11 months ago 69
Python Question

HEAD method not allowed after upgrading to django-rest-framework 3.5.3

We are upgrading django-rest-framework from 3.1.3 to 3.5.3. After the upgrade all of our ModelViewSet and viewsets.GenericViewSet views that utilize DefaultRouter to generate the urls no longer allow HEAD method calls. I've searched through the release notes and docs and haven't been able to find any setting or change that caused HEAD to stop being allowed.

I'm able to resolve this issue by subclassing the DefaultRouter and altering the route defaults, but I don't think this is the best or correct solution. From reading within django-rest-framework issues and documentation, it appears that django-rest-framework should handle HEAD and OPTIONS methods automatically.

@detail_route, @list_route, and views derived from ApiView which allow the GET method are automatically gaining the HEAD and OPTION methods.

Why has the HEAD method disappeared after this upgrade and what is the correct method of ensuring HEAD methods are allowed on our routes?

Our route and ModelViewSet definitions are very standard, here is a non working route:

from rest_framework.routers import DefaultRouter
from user_profile import views

router = DefaultRouter(trailing_slash=False)
router.register(r'user_names', views.UserNameView)

urlpatterns = router.urls

And the view:

class UserNameView(mixins.ListModelMixin,
queryset = User.objects.only(
"id", "first_name", "last_name", "email",
"mobile_phone", "photo", "is_active", "date_joined"
serializer_class = serializers.UserNameSerializer

Postman response to a HEAD call:

Status: 405 Method Not Allowed
Content-Type →application/json
Date →Wed, 09 Nov 2016 20:50:41 GMT
Server →WSGIServer/0.1 Python/2.7.12
Vary →Cookie
X-Frame-Options →SAMEORIGIN
x-xss-protection →1; mode=block

wim wim
Answer Source

You were apparently relying on an old behavior which was removed in release 3.5.0.

# Patch this in as it's otherwise only present from 1.5 onwards
if hasattr(self, 'get') and not hasattr(self, 'head'):
    self.head = self.get

Here is the related commit and github issue.

DefaultRouter does not include a HEAD route. You can add it into the routes, or specify it explictly using UserNameView.as_view(actions={'head': ...})