API Reference¶
Support for the HTTP QUERY method (RFC 10008) in Django REST framework.
This is a stop-gap until DRF supports QUERY itself
(encode/django-rest-framework#10091). It mirrors the names used there, so
moving to DRF's own support means removing this package rather than
rewriting views.
There are two ways to use it:
- Opt-in classes with the same names as DRF's, for example
drf_query_method.filters.SearchFilter, imported in place of DRF's. - :func:
patch, which applies the same behaviour to DRF itself, and to django-filter when it is installed.
add_query_method()
¶
Make Django before 6.2 handle QUERY as Django 6.2 does.
Django 6.2 supports QUERY natively, so this does nothing there. On
earlier versions it adds query to View.http_method_names, so any
view, including DRF's, can dispatch QUERY requests to a query()
handler, and it treats QUERY as a safe method in
CsrfViewMiddleware, so QUERY requests need no CSRF token. See
:func:skip_csrf_for_query.
Source code in src/drf_query_method/methods.py
filter_params(request)
¶
The parameters filter backends should read for request.
Uses request.filter_params when DRF provides it, natively or through
:func:drf_query_method.patch, and computes it otherwise. Custom filter
backends should read their parameters through this rather than
request.query_params, so that they support QUERY requests.
Source code in src/drf_query_method/params.py
patch()
¶
Add QUERY support to Django, DRF, django-filter and django-rest-framework-datatables in place.
Safe to call more than once. On Django versions before 6.2 this also
calls :func:~drf_query_method.add_query_method, which :func:unpatch
does not undo.
Source code in src/drf_query_method/patching.py
unpatch()
¶
Undo :func:patch, mainly for tests.
django_filters
¶
django-filter's DRF backend, reading QUERY request bodies.
Importing this module requires django-filter.
as_filter_data(params)
¶
Stringify a QUERY body's scalar values, as a query string would deliver them.
django-filter's widgets expect strings. JSON numbers and booleans become
"5" and "True", which its fields parse as usual, rather than
reaching widgets that call .split(",") on them.
Source code in src/drf_query_method/django_filters.py
filters
¶
DRF's SearchFilter and OrderingFilter, reading QUERY request bodies.
get_ordering is adapted from Django REST framework
(Copyright Encode OSS Ltd, BSD 3-Clause; see LICENSE).
get_ordering(self, request, queryset, view)
¶
Ordering comes from a comma delimited ?ordering=..., or a QUERY request's body.
Source code in src/drf_query_method/filters.py
get_search_terms(self, request)
¶
Search terms come from ?search=..., or a QUERY request's body, and may be whitespace delimited.
Source code in src/drf_query_method/filters.py
methods
¶
Make Django accept the QUERY method on versions before 6.2.
add_query_method()
¶
Make Django before 6.2 handle QUERY as Django 6.2 does.
Django 6.2 supports QUERY natively, so this does nothing there. On
earlier versions it adds query to View.http_method_names, so any
view, including DRF's, can dispatch QUERY requests to a query()
handler, and it treats QUERY as a safe method in
CsrfViewMiddleware, so QUERY requests need no CSRF token. See
:func:skip_csrf_for_query.
Source code in src/drf_query_method/methods.py
skip_csrf_for_query(process_view)
¶
Wrap CsrfViewMiddleware.process_view to accept QUERY like GET.
Django 6.2 adds QUERY to the methods the middleware exempts as safe
under RFC 9110. Earlier versions hard-code GET, HEAD, OPTIONS
and TRACE, so the wrapper sends QUERY down the same path: a request
already processed, or a csrf_exempt view, is left alone, and anything
else is accepted without a token. Every other method falls through to
Django unchanged.
DRF's SessionAuthentication checks CSRF through CSRFCheck, a
CsrfViewMiddleware subclass, so it follows the same rule.
Source code in src/drf_query_method/methods.py
params
¶
The parameters filter backends should read for a request.
filter_params(request)
¶
The parameters filter backends should read for request.
Uses request.filter_params when DRF provides it, natively or through
:func:drf_query_method.patch, and computes it otherwise. Custom filter
backends should read their parameters through this rather than
request.query_params, so that they support QUERY requests.
Source code in src/drf_query_method/params.py
merge_filter_params(request)
¶
Return request.query_params with a QUERY request's body merged over it.
For any method other than QUERY this is request.query_params
itself. A QUERY request carries its query in the request body, so the
parsed body is merged over the URL query parameters, and the body wins
for any key present in both. List values in a JSON body become multiple
values, as repeated query-string parameters do.
Raises ParseError (400) when the body is not an object of parameters,
such as a JSON array. The result is cached on the request, as packages
such as django-rest-framework-datatables read many parameters each.
Source code in src/drf_query_method/params.py
patching
¶
Apply QUERY support to DRF itself, and to django-filter when installed.
:func:patch makes DRF behave as if encode/django-rest-framework#10091 were
merged, so existing views, routers and settings pick up QUERY without
changing imports. When django-filter or django-rest-framework-datatables
is installed, they read QUERY request bodies too. Call it once at startup,
for example from an AppConfig.ready(). It does nothing to DRF once DRF supports QUERY
itself.
patch()
¶
Add QUERY support to Django, DRF, django-filter and django-rest-framework-datatables in place.
Safe to call more than once. On Django versions before 6.2 this also
calls :func:~drf_query_method.add_query_method, which :func:unpatch
does not undo.
Source code in src/drf_query_method/patching.py
unpatch()
¶
Undo :func:patch, mainly for tests.
permissions
¶
DRF permission classes that treat QUERY as a safe method.
has_object_permission is adapted from Django REST framework
(Copyright Encode OSS Ltd, BSD 3-Clause; see LICENSE).
routers
¶
DRF routers that route QUERY to a viewset's query() on the list route.
get_routes_keeping_query_actions(router, viewset, get_routes)
¶
Call get_routes, leaving QUERY unrouted when the viewset has an @action named query.
That action keeps its own route, and DRF would otherwise reject it for
clashing with the built-in query route.
Source code in src/drf_query_method/routers.py
has_query_action(viewset)
¶
Whether the viewset already has an @action named query.
with_query_route(routes)
¶
Map QUERY to query on the list route of routes.
without_query_route(routes)
¶
Remove the QUERY mapping from every route in routes.
Source code in src/drf_query_method/routers.py
test
¶
DRF's test request factory and client, with a .query() method.
client_query(self, path, data=None, format=None, content_type=None, follow=False, **extra)
¶
Send a QUERY request, encoding data as .post() does.
Source code in src/drf_query_method/test.py
factory_query(self, path, data=None, format=None, content_type=None, **extra)
¶
Build a QUERY request, encoding data as .post() does.
Source code in src/drf_query_method/test.py
viewsets
¶
DRF viewsets that only answer QUERY on routes that map it.
drop_unmapped_query(view)
¶
Stop a viewset answering QUERY on a route whose actions don't map it.
A viewset's query() method has the same name as the QUERY handler, so
without this every route of a viewset that defines query() would
answer QUERY, including detail routes.