Merge pull request #4672 from greyli/return-list
Allow to return list as JSON
This commit is contained in:
commit
559a8458c4
6 changed files with 62 additions and 26 deletions
|
|
@ -65,6 +65,9 @@ Unreleased
|
|||
``with client`` block. It will be cleaned up when
|
||||
``response.get_data()`` or ``response.close()`` is called.
|
||||
|
||||
- Allow returning a list from a view function, to convert it to a
|
||||
JSON response like a dict is. :issue:`4672`
|
||||
|
||||
|
||||
Version 2.1.3
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -681,22 +681,25 @@ The return value from a view function is automatically converted into
|
|||
a response object for you. If the return value is a string it's
|
||||
converted into a response object with the string as response body, a
|
||||
``200 OK`` status code and a :mimetype:`text/html` mimetype. If the
|
||||
return value is a dict, :func:`jsonify` is called to produce a response.
|
||||
The logic that Flask applies to converting return values into response
|
||||
objects is as follows:
|
||||
return value is a dict or list, :func:`jsonify` is called to produce a
|
||||
response. The logic that Flask applies to converting return values into
|
||||
response objects is as follows:
|
||||
|
||||
1. If a response object of the correct type is returned it's directly
|
||||
returned from the view.
|
||||
2. If it's a string, a response object is created with that data and
|
||||
the default parameters.
|
||||
3. If it's a dict, a response object is created using ``jsonify``.
|
||||
4. If a tuple is returned the items in the tuple can provide extra
|
||||
3. If it's an iterator or generator returning strings or bytes, it is
|
||||
treated as a streaming response.
|
||||
4. If it's a dict or list, a response object is created using
|
||||
:func:`~flask.json.jsonify`.
|
||||
5. If a tuple is returned the items in the tuple can provide extra
|
||||
information. Such tuples have to be in the form
|
||||
``(response, status)``, ``(response, headers)``, or
|
||||
``(response, status, headers)``. The ``status`` value will override
|
||||
the status code and ``headers`` can be a list or dictionary of
|
||||
additional header values.
|
||||
5. If none of that works, Flask will assume the return value is a
|
||||
6. If none of that works, Flask will assume the return value is a
|
||||
valid WSGI application and convert that into a response object.
|
||||
|
||||
If you want to get hold of the resulting response object inside the view
|
||||
|
|
@ -727,8 +730,8 @@ APIs with JSON
|
|||
``````````````
|
||||
|
||||
A common response format when writing an API is JSON. It's easy to get
|
||||
started writing such an API with Flask. If you return a ``dict`` from a
|
||||
view, it will be converted to a JSON response.
|
||||
started writing such an API with Flask. If you return a ``dict`` or
|
||||
``list`` from a view, it will be converted to a JSON response.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -741,20 +744,20 @@ view, it will be converted to a JSON response.
|
|||
"image": url_for("user_image", filename=user.image),
|
||||
}
|
||||
|
||||
Depending on your API design, you may want to create JSON responses for
|
||||
types other than ``dict``. In that case, use the
|
||||
:func:`~flask.json.jsonify` function, which will serialize any supported
|
||||
JSON data type. Or look into Flask community extensions that support
|
||||
more complex applications.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@app.route("/users")
|
||||
def users_api():
|
||||
users = get_all_users()
|
||||
return jsonify([user.to_json() for user in users])
|
||||
return [user.to_json() for user in users]
|
||||
|
||||
This is a shortcut to passing the data to the
|
||||
:func:`~flask.json.jsonify` function, which will serialize any supported
|
||||
JSON data type. That means that all the data in the dict or list must be
|
||||
JSON serializable.
|
||||
|
||||
For complex types such as database models, you'll want to use a
|
||||
serialization library to convert the data to valid JSON types first.
|
||||
There are many serialization libraries and Flask API extensions
|
||||
maintained by the community that support more complex applications.
|
||||
|
||||
|
||||
.. _sessions:
|
||||
|
|
|
|||
|
|
@ -1830,6 +1830,9 @@ class Flask(Scaffold):
|
|||
``dict``
|
||||
A dictionary that will be jsonify'd before being returned.
|
||||
|
||||
``list``
|
||||
A list that will be jsonify'd before being returned.
|
||||
|
||||
``generator`` or ``iterator``
|
||||
A generator that returns ``str`` or ``bytes`` to be
|
||||
streamed as the response.
|
||||
|
|
@ -1855,6 +1858,7 @@ class Flask(Scaffold):
|
|||
|
||||
.. versionchanged:: 2.2
|
||||
A generator will be converted to a streaming response.
|
||||
A list will be converted to a JSON response.
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
A dict will be converted to a JSON response.
|
||||
|
|
@ -1907,7 +1911,7 @@ class Flask(Scaffold):
|
|||
headers=headers, # type: ignore[arg-type]
|
||||
)
|
||||
status = headers = None
|
||||
elif isinstance(rv, dict):
|
||||
elif isinstance(rv, (dict, list)):
|
||||
rv = jsonify(rv)
|
||||
elif isinstance(rv, BaseResponse) or callable(rv):
|
||||
# evaluate a WSGI callable, or coerce a different response
|
||||
|
|
@ -1920,15 +1924,17 @@ class Flask(Scaffold):
|
|||
raise TypeError(
|
||||
f"{e}\nThe view function did not return a valid"
|
||||
" response. The return type must be a string,"
|
||||
" dict, tuple, Response instance, or WSGI"
|
||||
f" callable, but it was a {type(rv).__name__}."
|
||||
" dict, list, tuple with headers or status,"
|
||||
" Response instance, or WSGI callable, but it"
|
||||
f" was a {type(rv).__name__}."
|
||||
).with_traceback(sys.exc_info()[2]) from None
|
||||
else:
|
||||
raise TypeError(
|
||||
"The view function did not return a valid"
|
||||
" response. The return type must be a string,"
|
||||
" dict, tuple, Response instance, or WSGI"
|
||||
f" callable, but it was a {type(rv).__name__}."
|
||||
" dict, list, tuple with headers or status,"
|
||||
" Response instance, or WSGI callable, but it was a"
|
||||
f" {type(rv).__name__}."
|
||||
)
|
||||
|
||||
rv = t.cast(Response, rv)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@ if t.TYPE_CHECKING: # pragma: no cover
|
|||
|
||||
# The possible types that are directly convertible or are a Response object.
|
||||
ResponseValue = t.Union[
|
||||
"Response", str, bytes, t.Dict[str, t.Any], t.Iterator[str], t.Iterator[bytes]
|
||||
"Response",
|
||||
str,
|
||||
bytes,
|
||||
t.List[t.Any],
|
||||
t.Dict[str, t.Any],
|
||||
t.Iterator[str],
|
||||
t.Iterator[bytes],
|
||||
]
|
||||
|
||||
# the possible types for an individual HTTP header
|
||||
|
|
|
|||
|
|
@ -1166,6 +1166,10 @@ def test_response_types(app, client):
|
|||
def from_dict():
|
||||
return {"foo": "bar"}, 201
|
||||
|
||||
@app.route("/list")
|
||||
def from_list():
|
||||
return ["foo", "bar"], 201
|
||||
|
||||
assert client.get("/text").data == "Hällo Wörld".encode()
|
||||
assert client.get("/bytes").data == "Hällo Wörld".encode()
|
||||
|
||||
|
|
@ -1205,6 +1209,10 @@ def test_response_types(app, client):
|
|||
assert rv.json == {"foo": "bar"}
|
||||
assert rv.status_code == 201
|
||||
|
||||
rv = client.get("/list")
|
||||
assert rv.json == ["foo", "bar"]
|
||||
assert rv.status_code == 201
|
||||
|
||||
|
||||
def test_response_type_errors():
|
||||
app = flask.Flask(__name__)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,17 @@ def hello_bytes() -> bytes:
|
|||
|
||||
@app.route("/json")
|
||||
def hello_json() -> Response:
|
||||
return jsonify({"response": "Hello, World!"})
|
||||
return jsonify("Hello, World!")
|
||||
|
||||
|
||||
@app.route("/json/dict")
|
||||
def hello_json_dict() -> t.Dict[str, t.Any]:
|
||||
return {"response": "Hello, World!"}
|
||||
|
||||
|
||||
@app.route("/json/dict")
|
||||
def hello_json_list() -> t.List[t.Any]:
|
||||
return [{"message": "Hello"}, {"message": "World"}]
|
||||
|
||||
|
||||
@app.route("/generator")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue