summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorJake Howard <git@theorangeone.net>2024-07-26 12:34:42 +0100
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-09-09 12:02:18 +0200
commite161bd4657177f0e723a14a6e414884363b31a5d (patch)
treefbc9c862c2caad8cfa36537d108e1a9ddc281474 /docs
parent826ef006681eae1e9b4bd0e4f18fa13713025cba (diff)
Fixed #35631 -- Added HttpRequest.get_preferred_type().
Diffstat (limited to 'docs')
-rw-r--r--docs/ref/request-response.txt55
-rw-r--r--docs/releases/5.2.txt6
-rw-r--r--docs/topics/class-based-views/generic-editing.txt53
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.