Skip to content

Commit

Permalink
Fixed #17449 -- Added OPTIONS to generic views.
Browse files Browse the repository at this point in the history
Thanks estebistec for the report and patch.
  • Loading branch information
aaugustin committed May 17, 2012
1 parent 009e237 commit c4996df
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
12 changes: 10 additions & 2 deletions django/views/generic/base.py
Expand Up @@ -79,14 +79,22 @@ def dispatch(self, request, *args, **kwargs):
return handler(request, *args, **kwargs)

def http_method_not_allowed(self, request, *args, **kwargs):
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
extra={
'status_code': 405,
'request': self.request
}
)
return http.HttpResponseNotAllowed(allowed_methods)
return http.HttpResponseNotAllowed(self._allowed_methods())

def options(self, request, *args, **kwargs):
response = http.HttpResponse()
response['Allow'] = ', '.join(self._allowed_methods())
response['Content-Length'] = 0
return response

def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]


class TemplateResponseMixin(object):
Expand Down
52 changes: 52 additions & 0 deletions tests/regressiontests/generic_views/base.py
Expand Up @@ -173,6 +173,58 @@ def test_dispatch_decoration(self):
"""
self.assertTrue(DecoratedDispatchView.as_view().is_decorated)

def test_head_no_get(self):
"""
Test that a view class with no get responds to a HEAD request with HTTP
405.
"""
request = self.rf.head('/')
view = PostOnlyView.as_view()
self.assertEqual(405, view(request).status_code)

def test_options(self):
"""
Test that views respond to HTTP OPTIONS requests with an Allow header
appropriate for the methods implemented by the view class.
"""
request = self.rf.options('/')
view = SimpleView.as_view()
response = view(request)
self.assertEqual(200, response.status_code)
self.assertTrue(response['Allow'])

def test_options_for_get_view(self):
"""
Test that a view implementing GET allows GET and HEAD.
"""
request = self.rf.options('/')
view = SimpleView.as_view()
response = view(request)
self._assert_allows(response, 'GET', 'HEAD')

def test_options_for_get_and_post_view(self):
"""
Test that a view implementing GET and POST allows GET, HEAD, and POST.
"""
request = self.rf.options('/')
view = SimplePostView.as_view()
response = view(request)
self._assert_allows(response, 'GET', 'HEAD', 'POST')

def test_options_for_post_view(self):
"""
Test that a view implementing POST allows POST.
"""
request = self.rf.options('/')
view = PostOnlyView.as_view()
response = view(request)
self._assert_allows(response, 'POST')

def _assert_allows(self, response, *expected_methods):
"Assert allowed HTTP methods reported in the Allow response header"
response_allows = set(response['Allow'].split(', '))
self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows)


class TemplateViewTest(TestCase):
urls = 'regressiontests.generic_views.urls'
Expand Down

0 comments on commit c4996df

Please sign in to comment.