In previous blogs we saw about the basic syntax of python and after that we setup our first app on flask.
In this post to understand flask better, we will implement a simple todo app.
These are the routes that we will setp
GET /todo (return all todos)
POST /todo (add new todo)
PUT /todo/3 (update an existing todo with id 3)
DELETE /todo/3 (delete todo with id 3)
At this point it’s also important to understand what is rest api and what do these get, post, put, delete means if you are not already aware of it.
https://restfulapi.net/http-methods/
Now let’s get started with coding
GET Route
This is how the code looks and we will go through it step by step
from flask import Flask , jsonify
app = Flask(__name__)
tasks = [
{
'id': 1,
'title': 'Buy groceries',
'description': 'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': 'Learn Python',
'description': 'Need to find a good Python tutorial on the web',
'done': False
}
]
@app.route('/todo', methods=["GET"])
def todo():
return jsonify(tasks)
To explain things tasks = [] is array which we will use our data structure to store todos. You can learn more about lists and dict here
https://www.learnpython.org/en/Lists
https://www.learnpython.org/en/Dictionaries
http://flask.pocoo.org/docs/1.0/quickstart/#routing
you can find many more articles online to understand more about Lists, Dictionaries
Next, in all our routes we would we need to return a json response not text hence the library “jsonfiy” is used which converts array, dict to json.
Now if we run this application and open http://127.0.0.1:5000/todo on the browser you will see the json output.
Flask Debug Mode
At this point we should also look at flask debug mode. When we develop our flask application, we have to keep stopping and starting the flask app as we develop it. Rather it should automatically restart and take the latest code. To do this do
$ export FLASK_DEBUG=1
$ python -m flask run
Serving Flask app "hello.py" (lazy loading)
Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
Debug mode: on
Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Restarting with stat
Debugger is active!
Debugger PIN: 187-186-371
Now as you keep writing code, the flask app will keep restarting and even show errors.
POST Route
Let’s do this step by step. First we will simply define a route
@app.route('/todo', methods=["GET"])
def todo():
return jsonify(tasks)
@app.route('/todo', methods=["POST"])
def add_todo():
return ""
To test this route, we need to use a tool called postman
Since this is a POST route, we cannot just simply open this in browser, we need specific tool to fire such api. So install postman and from there we can do this easily.
Its important to note that spacing, indentation and line breaks are very important to python syntax or else python gives syntax error. Easiest way to is to avoid this is use VSCode formatting feature.
You need to import jsonify or any new packages we use. see the entire code base at the end of the blog
Now, if we open postman and test this route we should get status 200 which means this works.
Next, we need to add a new todo but first we need to add some validations and pass the todo data from postman.
Now our code looks like this
@app.route('/todo', methods=["POST"])
def add_todo():
if not request.json:
abort(500)
id = request.json.get("id", None)
title = request.json.get("title", None)
desc = request.json.get("description", "")
if id is None or title is None:
return jsonify(message="Invalid Request"), 500
return ""
In the code, we are first check if request has json else we throw error 500 using abort
Next we fetch and validate each parameter from request json and throw error if important parameter are missing and set status as 500 when we return the json.
Finally we need to append to the tasks array so code goes like this now
@app.route('/todo', methods=["POST"])
def add_todo():
if not request.json:
abort(500)
id = request.json.get("id", None)
title = request.json.get("title", None)
desc = request.json.get("description", "")
if id is None or title is None:
return jsonify(message="Invalid Request"), 500
tasks.append({
"id" : id,
"title" : title,
"description" : desc,
"done" : False
})
return jsonify(len(tasks))
PUT Route
Now we will see we can update the tasks array
so first lets setup the route
@app.route("/todo/<int:id>", methods=['PUT'])
def update_todo(id):
return ""
and test it on postman. Next add the standard validations we added before. Now we need to update the existing array, and for that we need to use python list comprehension. This is a very useful feature in python if you understand it well and can be used as replacement of map() and filter() from javascript.
Now our update code looks like this
def update(task, task_id, data):
if task["id"] == task_id:
if 'title' in data:
task["title"] = data['title']
if 'description' in data:
task["description"] = data["description"]
return task
@app.route("/todo/<int:id>", methods=['PUT'])
def update_todo(id):
if not request.json:
abort(500)
todo_id = request.json.get("id", None)
title = request.json.get("title", None)
desc = request.json.get("description", "")
if todo_id is None or title is None:
return jsonify(message="Invalid Request") , 500
global tasks
tasks = [update(task, id, request.json) for task in tasks]
return jsonify(tasks)
Go through the above code in detail, its important to understand python list comprehension and you can find many blogs about it online as well.
DELETE Route
Now, finally in we are left with delete todo for which we will again use list comprehension
@app.route("/todo/<int:id>", methods=["DELETE"])
def delete_todo(id):
global tasks
tasks = [task for task in tasks if task["id"] != id]
return jsonify(tasks)
This finishes our basic todo app. At this stage, it’s important to sink your head in what you read about and try different things to get a good grasp of it.
Here is the entire final code for reference
from flask import Flask, jsonify, abort, request
app = Flask(__name__)
tasks = [
{
'id': 1,
'title': 'Buy groceries',
'description': 'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': 'Learn Python',
'description': 'Need to find a good Python tutorial on the web',
'done': False
}
]
@app.route('/todo', methods=["GET"])
def todo():
return jsonify(tasks)
@app.route('/todo', methods=["POST"])
def add_todo():
if not request.json:
abort(500)
id = request.json.get("id", None)
title = request.json.get("title", None)
desc = request.json.get("description", "")
if id is None or title is None:
return jsonify(message="Invalid Request") , 500
tasks.append({
"id": id,
"title": title,
"description": desc,
"done": False
})
return jsonify(len(tasks))
def update(task, task_id, data):
if task["id"] == task_id:
if 'title' in data:
task["title"] = data['title']
if 'description' in data:
task["description"] = data["description"]
return task
@app.route("/todo/<int:id>", methods=['PUT'])
def update_todo(id):
if not request.json:
abort(500)
todo_id = request.json.get("id", None)
title = request.json.get("title", None)
desc = request.json.get("description", "")
if todo_id is None or title is None:
return jsonify(message="Invalid Request") , 500
global tasks
tasks = [update(task, id, request.json) for task in tasks]
return jsonify(tasks)
@app.route("/todo/<int:id>", methods=["DELETE"])
def delete_todo(id):
global tasks
tasks = [task for task in tasks if task["id"] != id]
return jsonify(tasks)