Ticket #17449: patch17449.diff

File patch17449.diff, 5.3 KB (added by estebistec, 4 years ago)

Updated patch for improved HEAD, and default OPTIONS

  • docs/ref/request-response.txt

     
    761761    Like :class:`HttpResponse`, but uses a 405 status code. Takes a single,
    762762    required argument: a list of permitted methods (e.g. ``['GET', 'POST']``).
    763763
     764.. class:: HttpResponseOptions
     765
     766    Like :class:`HttpResponse`, but uses a 204 status code and provides
     767    an ``Allow`` response header. Takes a single, required argument: a list
     768    of permitted methods (e.g., ``['GET', 'POST']``) used to populate
     769    the ``Allow`` header.
     770
    764771.. class:: HttpResponseGone
    765772
    766773    Acts just like :class:`HttpResponse` but uses a 410 status code.
  • django/http/__init__.py

     
    720720            raise Exception("This %s instance cannot tell its position" % self.__class__)
    721721        return sum([len(str(chunk)) for chunk in self._container])
    722722
     723class HttpResponseOptions(HttpResponse):
     724    status_code = 204
     725
     726    def __init__(self, permitted_methods):
     727        super(HttpResponseOptions, self).__init__()
     728        self['Allow'] = ', '.join(permitted_methods)
     729        self['Content-Length'] = '0'
     730
    723731class HttpResponseRedirect(HttpResponse):
    724732    status_code = 302
    725733
  • django/views/generic/base.py

     
    4343
    4444        def view(request, *args, **kwargs):
    4545            self = cls(**initkwargs)
     46            if hasattr(self, 'get') and not hasattr(self, 'head'):
     47                self.head = self.get
    4648            return self.dispatch(request, *args, **kwargs)
    4749
    4850        # take name and docstring from class
     
    6769        return handler(request, *args, **kwargs)
    6870
    6971    def http_method_not_allowed(self, request, *args, **kwargs):
    70         allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
     72        allowed_methods = [m.upper() for m in self.http_method_names
     73                if hasattr(self, m)]
    7174        logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path),
    7275            extra={
    7376                'status_code': 405,
     
    7679        )
    7780        return http.HttpResponseNotAllowed(allowed_methods)
    7881
    79     def head(self, *args, **kwargs):
    80         return self.get(*args, **kwargs)
     82    def options(self, request, *args, **kwargs):
     83        allowed_methods = [m.upper() for m in self.http_method_names
     84                if hasattr(self, m)]
     85        return http.HttpResponseOptions(allowed_methods)
    8186
    8287
    8388class TemplateResponseMixin(object):
  • tests/regressiontests/generic_views/base.py

     
    5555        return self
    5656
    5757
     58class PostOnlyView(View):
     59    def post(self, request):
     60        return HttpResponse('This view only accepts POST')
     61
     62
    5863class ViewTest(unittest.TestCase):
    5964    rf = RequestFactory()
    6065
     
    157162        """
    158163        self.assertTrue(DecoratedDispatchView.as_view().is_decorated)
    159164
     165    def test_head_no_get(self):
     166        """
     167        Test that a view class with no get responds to a HEAD request with HTTP
     168        405.
     169        """
     170        request = self.rf.head('/')
     171        view = PostOnlyView.as_view()
     172        self.assertEqual(405, view(request).status_code)
    160173
     174    def test_options(self):
     175        """
     176        Test that views respond to HTTP OPTIONS requests with an Allow header
     177        appropriate for the methods implemented by the view class.
     178        """
     179        request = self.rf.options('/')
     180        view = SimpleView.as_view()
     181        response = view(request)
     182        self.assertEqual(204, response.status_code)
     183        self.assertTrue(response['Allow'])
     184
     185    def test_options_for_get_view(self):
     186        """
     187        Test that a view implementing GET allows GET and HEAD.
     188        """
     189        request = self.rf.options('/')
     190        view = SimpleView.as_view()
     191        response = view(request)
     192        self._assert_allows(response, 'GET', 'HEAD')
     193
     194    def test_options_for_get_and_post_view(self):
     195        """
     196        Test that a view implementing GET and POST allows GET, HEAD, and POST.
     197        """
     198        request = self.rf.options('/')
     199        view = SimplePostView.as_view()
     200        response = view(request)
     201        self._assert_allows(response, 'GET', 'HEAD', 'POST')
     202
     203    def test_options_for_post_view(self):
     204        """
     205        Test that a view implementing POST allows POST.
     206        """
     207        request = self.rf.options('/')
     208        view = PostOnlyView.as_view()
     209        response = view(request)
     210        self._assert_allows(response, 'POST')
     211
     212    def _assert_allows(self, response, *expected_methods):
     213        "Assert allowed HTTP methods reported in the Allow response header"
     214        response_allows = set(response['Allow'].split(', '))
     215        self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows)
     216
    161217class TemplateViewTest(TestCase):
    162218    urls = 'regressiontests.generic_views.urls'
    163219
Back to Top