Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Unreleased
it's disabled in config. Previously, only disabling worked. :issue:`5916`
- ``Flask.select_jinja_autoescape`` uses case-insensitive comparison instead
of only lower case file extensions. :pr:`6012`
- Add ``query`` method route shortcut for the ``QUERY`` HTTP method
(:rfc:`10008`). ``MethodView`` dispatches ``QUERY`` requests to a ``query``
handler. :issue:`6065`


Version 3.1.3
Expand Down
11 changes: 11 additions & 0 deletions src/flask/sansio/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,17 @@ def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
"""
return self._method_route("PATCH", rule, options)

@setupmethod
def query(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
"""Shortcut for :meth:`route` with ``methods=["QUERY"]``.
The ``QUERY`` method is a safe, idempotent request method that
carries content in the request body, defined in :rfc:`10008`.
.. versionadded:: 3.2
"""
return self._method_route("QUERY", rule, options)

@setupmethod
def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
"""Decorate a view function to register it with the given URL
Expand Down
2 changes: 1 addition & 1 deletion src/flask/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
F = t.TypeVar("F", bound=t.Callable[..., t.Any])

http_method_funcs = frozenset(
["get", "post", "head", "options", "delete", "put", "trace", "patch"]
["get", "post", "head", "options", "delete", "put", "trace", "patch", "query"]
)


Expand Down
13 changes: 13 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ def test_method_route_no_methods(app):
app.get("/", methods=["GET", "POST"])


def test_query_method_route(app, client):
@app.query("/")
def query():
return flask.request.get_data(as_text=True)

rv = client.open("/", method="QUERY", data="select=*")
assert rv.status_code == 200
assert rv.data == b"select=*"
# The QUERY method is advertised like any other registered method.
rv = client.open("/", method="OPTIONS")
assert "QUERY" in rv.allow


def test_provide_automatic_options_attr_disable(
app: flask.Flask, client: FlaskClient
) -> None:
Expand Down
14 changes: 14 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ def post(self):
common_test(app)


def test_query_method_view(app, client):
class Index(flask.views.MethodView):
def query(self):
return flask.request.get_data(as_text=True)

app.add_url_rule("/", view_func=Index.as_view("index"))

assert Index.methods == {"QUERY"}
rv = client.open("/", method="QUERY", data="select=*")
assert rv.status_code == 200
assert rv.data == b"select=*"
assert client.get("/").status_code == 405


def test_view_patching(app):
class Index(flask.views.MethodView):
def get(self):
Expand Down
Loading