diff options
| author | Jake Howard <git@theorangeone.net> | 2024-07-26 12:34:42 +0100 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2024-09-09 12:02:18 +0200 |
| commit | e161bd4657177f0e723a14a6e414884363b31a5d (patch) | |
| tree | fbc9c862c2caad8cfa36537d108e1a9ddc281474 /docs | |
| parent | 826ef006681eae1e9b4bd0e4f18fa13713025cba (diff) | |
Fixed #35631 -- Added HttpRequest.get_preferred_type().
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/ref/request-response.txt | 55 | ||||
| -rw-r--r-- | docs/releases/5.2.txt | 6 | ||||
| -rw-r--r-- | docs/topics/class-based-views/generic-editing.txt | 53 |
3 files changed, 101 insertions, 13 deletions
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 20c04279b2..31111a435a 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -425,10 +425,48 @@ Methods Returns ``True`` if the request is secure; that is, if it was made with HTTPS. +.. method:: HttpRequest.get_preferred_type(media_types) + + .. versionadded:: 5.2 + + Returns the preferred mime type from ``media_types``, based on the + ``Accept`` header, or ``None`` if the client does not accept any of the + provided types. + + Assuming the client sends an ``Accept`` header of + ``text/html,application/json;q=0.8``: + + .. code-block:: pycon + + >>> request.get_preferred_type(["text/html", "application/json"]) + "text/html" + >>> request.get_preferred_type(["application/json", "text/plain"]) + "application/json" + >>> request.get_preferred_type(["application/xml", "text/plain"]) + None + + Most browsers send ``Accept: */*`` by default, meaning they don't have a + preference, in which case the first item in ``media_types`` would be + returned. + + Setting an explicit ``Accept`` header in API requests can be useful for + returning a different content type for those consumers only. See + :ref:`content-negotiation-example` for an example of returning + different content based on the ``Accept`` header. + + .. note:: + + If a response varies depending on the content of the ``Accept`` header + and you are using some form of caching like Django's + :mod:`cache middleware <django.middleware.cache>`, you should decorate + the view with :func:`vary_on_headers('Accept') + <django.views.decorators.vary.vary_on_headers>` so that the responses + are properly cached. + .. method:: HttpRequest.accepts(mime_type) - Returns ``True`` if the request ``Accept`` header matches the ``mime_type`` - argument: + Returns ``True`` if the request's ``Accept`` header matches the + ``mime_type`` argument: .. code-block:: pycon @@ -436,17 +474,10 @@ Methods True Most browsers send ``Accept: */*`` by default, so this would return - ``True`` for all content types. Setting an explicit ``Accept`` header in - API requests can be useful for returning a different content type for those - consumers only. See :ref:`content-negotiation-example` of using - ``accepts()`` to return different content to API consumers. + ``True`` for all content types. - If a response varies depending on the content of the ``Accept`` header and - you are using some form of caching like Django's :mod:`cache middleware - <django.middleware.cache>`, you should decorate the view with - :func:`vary_on_headers('Accept') - <django.views.decorators.vary.vary_on_headers>` so that the responses are - properly cached. + See :ref:`content-negotiation-example` for an example of using + ``accepts()`` to return different content based on the ``Accept`` header. .. method:: HttpRequest.read(size=None) .. method:: HttpRequest.readline() diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index add5d9506a..3dd7b00b29 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -226,7 +226,8 @@ Models Requests and Responses ~~~~~~~~~~~~~~~~~~~~~~ -* ... +* The new :meth:`.HttpRequest.get_preferred_type` method can be used to query + the preferred media type the client accepts. Security ~~~~~~~~ @@ -309,6 +310,9 @@ Miscellaneous * The minimum supported version of ``gettext`` is increased from 0.15 to 0.19. +* ``HttpRequest.accepted_types`` is now sorted by the client's preference, based + on the request's ``Accept`` header. + .. _deprecated-features-5.2: Features deprecated in 5.2 diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 5841c703f6..4310ae9dcc 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -273,3 +273,56 @@ works with an API-based workflow as well as 'normal' form POSTs:: class AuthorCreateView(JsonableResponseMixin, CreateView): model = Author fields = ["name"] + +The above example assumes that if the client supports ``text/html``, that they +would prefer it. However, this may not always be true. When requesting a +``.css`` file, many browsers will send the header +``Accept: text/css,*/*;q=0.1``, indicating that they would prefer CSS, but +anything else is fine. This means ``request.accepts("text/html") will be +``True``. + +To determine the correct format, taking into consideration the client's +preference, use :func:`django.http.HttpRequest.get_preferred_type`:: + + class JsonableResponseMixin: + """ + Mixin to add JSON support to a form. + Must be used with an object-based FormView (e.g. CreateView). + """ + + accepted_media_types = ["text/html", "application/json"] + + def dispatch(self, request, *args, **kwargs): + if request.get_preferred_type(self.accepted_media_types) is None: + # No format in common. + return HttpResponse( + status_code=406, headers={"Accept": ",".join(self.accepted_media_types)} + ) + + return super().dispatch(request, *args, **kwargs) + + def form_invalid(self, form): + response = super().form_invalid(form) + accepted_type = request.get_preferred_type(self.accepted_media_types) + if accepted_type == "text/html": + return response + elif accepted_type == "application/json": + return JsonResponse(form.errors, status=400) + + def form_valid(self, form): + # We make sure to call the parent's form_valid() method because + # it might do some processing (in the case of CreateView, it will + # call form.save() for example). + response = super().form_valid(form) + accepted_type = request.get_preferred_type(self.accepted_media_types) + if accepted_type == "text/html": + return response + elif accepted_type == "application/json": + data = { + "pk": self.object.pk, + } + return JsonResponse(data) + +.. versionchanged:: 5.2 + + The :meth:`.HttpRequest.get_preferred_type` method was added. |
