diff --git a/CHANGES.rst b/CHANGES.rst index 825b2e27..504e319f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 ------------- diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7c6c594c..ed5f4557 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -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: diff --git a/src/flask/app.py b/src/flask/app.py index f9db3b00..8726a6e8 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1924,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, list, 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, list, 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)