On Thu, Nov 21, 2019 at 3:03 AM Jure Erznožnik <[email protected]>
wrote:

> If possible, could the logic determining "the best match for your options"
> be overridable?
>
Actually it is quite straightforward, the client set in the header of its
request all the media types it wants ("accepts as a response") with a
preference parameter, i.e. "Accepts:JSON;q=0.9,XML;q=1,*/*;q=0.8" for
wanting XML, if not JSON, if not anything else (the client's demand
preferences).

You previously decide which one you support on your view, i.e. this view
will deliver its content in HTML and JSON - it doesn't matter which one,
that's what is available as a delivery (your offering).

Then you have to match which one of your offerings will better satisfy the
client demand expressed by its preferences. What's best for you is already
set when you express your preferences for the request in the header, the
problem is that you may not have what is best for the client - but you may
have something that satisfies it better than others: you don't have XML but
you have JSON which it preffers to the rest.

It is the best match in that sense: given the client request preferences,
respond with the more "satisfying" offering available.

This is done (mostly) to avoid a request, instead of doing a request for
the available formats and do another with the choosen one, you request with
the ones that makes you happy and then let the server find the best way to
serve you, and for the spec writers resembles a negotiation ("I want A, B,
C or D - I can give you B or D - Then D - OK").

If you filter the client's demand with your offering (you intersect them),
i.e. "Accepts:JSON;q=0.9,XML;q=1,*/*;q=0.8" \Int "JSON, HTML" ->
"Accepts:JSON;q=0.9,HTML;q=0.8", and then you order them, the one that you
should serve would be always the first one, because if there is a tie, it
is the same for the client - the client chose that the server choose
between them (though you can specify a default media serving ordering that
will handle also no preferences).

That's why "Just parsing them it and make them available in the request -
filtered by the supported ones - as an ordered list
("accepted_media_types") and the preferred one ("preferred_media_type")
seems sufficient to me."

With that API, you could implement easily content negotiation, both in
functional views and CBVs. In CBVs, you can go further and use
metaprograming techniques (like the ones used for "get_FOO_display()") to
make the methods available "on the fly": "to_LABEL" and have the dispatcher
choose the method according to "preferred_media_type"  - although making
that explicit by the user may not be bad at all.

Note that this is mostly needed in REST APIs, where an endpoint is exposed
to several clients which may have different requirements - i.e. a table in
CSV, XML, etc. But for many Django applications this would be between
browsers and the server, and between HTML and JSON if AJAX.

As converting to JSON it may seems straightforward, it may not - that's why
DRF exists and addresses this problem in an excellent way (IMO :), so the
user in "plain" Django will have to deal with serialization (
https://docs.djangoproject.com/en/2.2/topics/serialization/) and not much
besides defaulting to DjangoJsonEncoder for the context in CVB and for
functional views (like https://github.com/jsocol/django-jsonview) can be
done - at least that comes to my mind - for giving convenience :)

That way standard implementation would cater for 80/20 and everyone would
> still have an option to customise further.
>
> LP,
> Jure
> On 21/11/2019 02:22, Matemática A3K wrote:
>
>
>
> On Wed, Nov 20, 2019 at 11:52 PM James Bennett <[email protected]>
> wrote:
>
>> On Wed, Nov 20, 2019 at 3:44 PM Curtis Maloney <[email protected]>
>> wrote:
>>
>>>
>>> Yeah, I expected DRF had this "solved" already. From my own
>>> experimentation, mapping `cgi.parse_header` over the the "Accept" header
>>> value, split by comma, gets a usable result; then sort that list by 'q'
>>> (defaulting to 1.0) and you have your priority.
>>>
>>
>> Both the original and forks seem to've been abandoned now, but the
>> mimeparse library encapsulated this into a nice little function that took
>> an Accept header, and a list of content-types you could support in the
>> response, and told you which one was the best match. The code's still out
>> there and under a permissive license if somebody wants to pick it up again.
>>
>
> I think now that providing alternatives is the way to go :)
>
> When 'is_ajax' is used for content negotiation, then proper content
> negotiation via the Accept headers should be done.
>
> When 'is_ajax' is used for different content delivery (like routing), then
> make the convention explicit or refactoring should be done.
>
> In the case of content negotiation -
> https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation -
> this is only about media types (Accept-Language is already parsed in i18n).
>
> Just parsing them it and make them available in the request - filtered by
> the supported ones - as an ordered list ("accepted_media_types") and the
> preferred one ("preferred_media_type") seems sufficient to me.
>
> For functional views, it would be up to you to decide which one you
> support and how it is delivered with something like "if 'json' in
> request.preferred_media_type:".  We can add decorators for setting the
> response type for a specific media type and optionally returning a 406.
>
> For CBVs, a mixin should be done - something like ContentNegotiationMixin
> - where you define the the types you want to support in it (or otherwise
> use the settings) and you should define or override methods like "to_JSON",
> "to_XML", "to_LABEL" that will serialize your context into the media type
> that is the best match for your options.
>
> As more than one media type may correspond to one format, if a dict that
> labels the supported types is defined, something like:
>
> SUPPORTED_MEDIA_TYPES_LABELS = {
>   "application/json": "JSON",
>   "text/json": "JSON",
>   "application/pdf": "PDF",
>   "text/html": "HTML",
> }
>
> All the filtering can be easily done.
>
> If so, the deprecation warning should be something like:
> DeprecationWarning: Request.is_ajax is deprecated. Given that the
> X-Requested-With header is not a part of any spec, it is not reliable. If
> you are doing content negotiation, see ..docs/media_type_negotiation. If
> you are serving different content if the requests are made via AJAX, choose
> a convention for *your* project to discern them or consider refactoring
> your code (making your views specific to one intent for each verb).
>
> If you agree - in general - with this direction, I can find time to
> implement it.
>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Django developers (Contributions to Django itself)" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/django-developers/CAL13Cg9x9ZWM0LTLoMMF%3DxgMydqrOKhEnhsRn-miFkVk5Rx6tg%40mail.gmail.com
>> <https://groups.google.com/d/msgid/django-developers/CAL13Cg9x9ZWM0LTLoMMF%3DxgMydqrOKhEnhsRn-miFkVk5Rx6tg%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/CA%2BFDnhKbzzst8PxCr8q26UfYzLAZKi6fqQV9nUjpotkFb%2Byo1w%40mail.gmail.com
> <https://groups.google.com/d/msgid/django-developers/CA%2BFDnhKbzzst8PxCr8q26UfYzLAZKi6fqQV9nUjpotkFb%2Byo1w%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/9c3dd791-bed0-6c73-d40a-0462c4e93a1a%40gmail.com
> <https://groups.google.com/d/msgid/django-developers/9c3dd791-bed0-6c73-d40a-0462c4e93a1a%40gmail.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CA%2BFDnhJ3szWfZeXvtxV2UA16AaEiuGfsvK0dUwSr442K0Y-oNg%40mail.gmail.com.

Reply via email to