21

I'm creating an API using Flask and have the following code:

@app.route('/<major>/')
def major_res(major):
    course_list = list(client.db.course_col.find({"major": (major.encode("utf8", "ignore").upper())}))
    return json.dumps(course_list, sort_keys=True, indent=4, default=json_util.default)

When viewing /csci/ in the browser, the output looks like this:

[{ "course": "CSCI052", "description": "Fundamentals of Computer Science. A solid foundation in functional programming, procedural and data abstraction, recursion and problem-solving. Applications to key areas of computer science, including algorithms and complexity, computer architecture and organization, programming languages, finite automata and computability. This course serves the same role as HM 60 as a prerequisite for upper-division computer science courses at any of the Claremont Colleges. Prerequisite: 51.", "instructor": "Bull, Everett L.,, Jr.", "name": " Fundamentals of Computer Science", "number": 52, "school": "PO" }]

How do I return this dictionary so that each key and value are on their own line?

davidism
  • 121,510
  • 29
  • 395
  • 339
Aloke Desai
  • 1,029
  • 8
  • 17
  • 27

2 Answers2

36

Flask provides jsonify() as a convenience:

from flask import jsonify

@app.route("/<major>/")
def major_res(major):
    course_list = list(client.db.course_col.find({"major": major.upper()}))
    return jsonify(**course_list)

This will return the args of jsonify as a JSON representation, and, unlike your code, will send the proper Content-Type header: application/json. Take note of what the docs say about the format:

This function's response will be pretty printed if the JSONIFY_PRETTYPRINT_REGULAR config parameter is set to True or the Flask app is running in debug mode. Compressed (not pretty) formatting currently means no indents and no spaces after separators.

Responses will receive non-pretty-printed JSON when not in debug mode. This shouldn't be a problem since JSON for JavaScript consumption shouldn't need to be formatted (that's just extra data to be sent over the wire), and most tools format received JSON on their own.

If you'd like to still use json.dumps(), you can send the proper mimetype by returning a Response with current_app.response_class().

from flask import json, current_app

@app.route("/<major>/")
def major_res(major):
    course_list = list(client.db.course_col.find({"major": major.upper() }))
    return current_app.response_class(json.dumps(course_list), mimetype="application/json")

For more on the difference:


Prior to Flask 1.0, JSON handling was somewhat different. jsonify would try to detect whether a request was AJAX and return pretty printed if it was not; this was removed because it was unreliable. jsonify only allowed dicts as the top-level object for security reasons; this is no longer applicable in modern browsers.

asherbret
  • 5,439
  • 4
  • 38
  • 58
Bailey Parker
  • 15,599
  • 5
  • 53
  • 91
3

If for some reason you need to over-ride flask.jsonify (E.g., adding a custom json encoder) you can do so with the following method that implements the security fix @phpmycoder mentioned:

from json import dumps
from flask import make_response

def jsonify(status=200, indent=4, sort_keys=True, **kwargs):
    response = make_response(dumps(dict(**kwargs), indent=indent, sort_keys=sort_keys))
    response.headers['Content-Type'] = 'application/json; charset=utf-8'
    response.headers['mimetype'] = 'application/json'
    response.status_code = status
    return response

@app.route('/<major>/')
def major_res(major):
    course = client.db.course_col.find({"major": (major.encode("utf8", "ignore").upper())})
    return jsonify(**course)

@app.route('/test/')
def test():
    return jsonify(indent=2, sort_keys=False, result="This is just a test")

Response:

{
    "course": "CSCI052", 
    "description": "Fundamentals of Computer Science. A solid foundation in functional programming, procedural and data abstraction, recursion and problem-solving. Applications to key areas of computer science, including algorithms and complexity, computer architecture and organization, programming languages, finite automata and computability. This course serves the same role as HM 60 as a prerequisite for upper-division computer science courses at any of the Claremont Colleges. Prerequisite: 51.", 
    "instructor": "Bull, Everett L.,, Jr.", 
    "name": " Fundamentals of Computer Science", 
    "number": 52, 
    "school": "PO"
}
reubano
  • 5,087
  • 1
  • 42
  • 41
  • Since only one course is returned, you don't need a list. If you have another route that returns multiple courses, you can do this: `courses = query.that.returns.list` and `return jsonify(courses=courses)` – reubano Apr 27 '14 at 07:34