First time with Flask-Python -Module3

Building my first Flask API

Module 3 :

Python | Flask API | POST request with a clean Sanitized Data

Welcome back,

This is our third module of building our flask API. In our previous modules we built our first flask application using Python, developed our route decorator for GET method and did POST request with a clean Sanitized Data which returns a meaningful Status Code and Response.

In this module, we will discuss the following:

3.UPDATING DATA

3.1 Adding a PUT route

3.2 Getting the Client’s request

3.3 Sending PUT request to Flask

3.4 Finishing up the PUT route

3.5 Adding a PATCH route

3.6 Defining our PATCH route

3.7 Finishing up our PATCH route

By the end of this module, you will be able to do PUT and PATCH requests to update the Data.

Lets get inside..

3.UPDATING DATA

3.1 Adding a PUT route

We’ll start adding code to allow users to update data.

To do this, we need to add a route. This will be the PUT rout­e. This route will point to the same endpoint, as our previous one. We will define the method that should run whenever a client puts to our route.

# Adding a PUT route

#PUT route

@app.route(‘/books/<int:isbn>’, methods=[‘PUT’])

def replace_book(isbn):

pass #we will define it later

Code Snippet_3.1- Adding a PUT route:

# 3.1 Adding a PUT route

#PUT route
@app.route('/books/<int:isbn>', methods=['PUT'])
def replace_book(isbn):
	pass #we will define it later

We defined the method as replace_book(), that takes the ISBN that was passed, and we put this as a pass for now and we’ll define it shortly, and for now we Run and check the added route is working.

3.2 Getting the Client’s Request body

Client needs to send all the details of the book they want to add in the request body as an object.

Request body should be like this:

{

‘name’: ‘NewName’,

‘price’: 456

}

Note:

Incase the Client wants to update a single field, we can use PATCH request(will discuss in detail, shortly)

i.e., for partial data update, we use a different HTTP method called PATCH method but for PUT request, we provide the full information (all data as object) needed by the Resource.

#PUT route

@app.route(‘/books/<int:isbn>’, methods=[‘PUT’])

def replace_book(isbn):

return jsonify(request.get_json()) #just checking whether we can get back the Data

As want to verify that we can indeed get the request body that our client sends to us by displaying it back to them that this is defined, we need to return this and then we need to convert this using jsonify, and that will set the correct headers back as application/json to the client.

Code Snippet_3.2- Getting the Client’s Request body:

#3.2-Getting the Client's Request body
#PUT route
@app.route('/books/<int:isbn>', methods=['PUT'])
def replace_book(isbn):
	return jsonify(request.get_json()) 

3.3 Sending PUT request to Flask (checking in Postman)

Open up Postman and keep our server running. At firstwe do a GET request to our books. On Postman by pressing the plus icon, you can create a new request to use PUT. And for the body, this is an important point when using Postman, when you’re sending JSON, you want to put the body as raw. Enter the body, as shown in below and add JSON .

{
 'name': 'NewName',
 'price': 456	
}

CLI command and Output:

$ python app.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Postman :

Step1:

Fire a GET request, view books and take an isbn no from the list.

Step2:

Add new Request(PUT) at URL http://127.0.0.1:5000/books/123456(isbn)

Header->ContentType- application/JSON

Body-> raw->application/JSON

Request body:

{

“name”: “Book1”,

“price”: 15.5

}

After send this PUT request, what we should expect to see is the body that we had sent back to us, and a 200 status OK, if we drag the Response window up, we must see the body same as the Request body we sent and status: 200 status OK and the content type of application/json.

Step3:

Press Send button

View the Response window at the bottom

ResponseBody:

{

“name”: “Book1”,

“price”: 15.5

}

Ensure Response body is same as the request body(application/JSON).

3.4 Finishing up the PUT route

Runnable Code Snippet_3.4- Finishing up the PUT route:

# 3.4 Finishing up the PUT route

from flask import Flask, jsonify,request,Response

app = Flask(__name__)
print(__name__)

books=[
	{
		'name' : 'Book1',
		'price' : 15.5,
		'isbn' : 123456
	},
	{
		'name' : 'Book2',
		'price' : 12.2,
		'isbn' : 789654
	}
]

#request body
#{
#	'name' : 'Newbook',
#	'price' : 456,
#	'isbn' : 369852
#}


#data sanitizer
def validBookObject(bookObject):
	#if entry already exists
	for i in books:
		if i["isbn"] == bookObject["isbn"]:
			return False
		else:
			#if "keyword" in dictionary	
			if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
				return True
			else:
				return False	

#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
	request_data = request.get_json()#get the req.data by get_json method 
	if(validBookObject(request_data)): #considering only validObject is sent
		new_book = {
			"name": request_data['name'],
			"price": request_data['price'],
			"isbn": request_data['isbn']
		}
		books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
		response = Response("",201,mimetype='application/json')
		response.headers['Location'] = "/books/" + str(new_book['isbn'])
		return response
	else:
		invalidBookObjectErrorMsg = {
			"error" : "Invalid book object in request",
			"helpString" : "Pls pass Data in similar format {'name':'bookname', 'price':5.9, 'isbn':852569}" 
		}
		response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype='application/json')
		return response

#GET/books
@app.route('/books')
def get_all_books():
	return jsonify({'Books':books})

#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
	return_value={}
	for book in books:
		if book["isbn"] == isbn:
			return_value={
				'name' : book["name"],
				'price' : book["price"]
			}
	return jsonify(return_value)

#PUT /books/5869
#{
# 'name': 'NewName',
# 'price': 456	
#}
"""
to update a single field ,we use PATCH request(will discuss later)
"""

#PUT route
@app.route('/books/<int:isbn>', methods=['PUT'])
def replace_book(isbn):
	request_data=request.get_json()
	new_book = {
		'name' : request_data['name'],
		'price' : request_data['price'],
		'isbn' : isbn
	}
	i = 0;
	for book in books:
		currentIsbn=book["isbn"] #if book["isbn"]==isbn:
		if currentIsbn == isbn:
			books[i] = new_book
		i += 1
	response = Response("", status=204) #No content created
	return response
	
app.run(port=5000)

CLI command and Output:

$ python RSapp.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Postman:

Step1:

fire a GET request at http://127.0.0.1:5000/books

Request Body:

{
    "Books": [
        {
            "isbn": 123456,
            "name": "Book1",
            "price": 15.5
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

Step2:

fire a PUT request URL:http://127.0.0.1:5000/books/123456

Ensure you have enabled the type as follows: raw-application/JSON | Header-ContentType application/JSON

PUT requestbody:

{
    "name" : "UpdatedBook",
    "price" : 3.5
}

press SEND button

Step3:

Check the Response window

Status: 204 NO CONTENT

ResponseBody:

empty

Step4:

GET request again at http://127.0.0.1:5000/books

{
    "Books": [
        {
            "isbn": 123456,         <===Updated fields(got from URL!!)
            "name": "UpdatedBook",  <===Updated fields
            "price": 3.5	    <===Updated fields	
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

We can see the updated list from the above window, as expected.

3.5 Adding a PATCH route (and DataSanitizer for the previous PUT route)

Before getting into PATCH route, we clear up one more thing, that is adding a Data Sanitizer for our previous PUT route.

Code Snippet_3.5- Data sanitizing in ‘PUT’ method:

#add this snippet to sanitize the data in PUT method

#PUT route
@app.route('/books/<int:isbn>', methods=['PUT'])
def replace_book(isbn):
	request_data=request.get_json()
	if(not valid_put_request_data(request_data)):  <===this snippet
		invalidBookObjectErrorMsg = {
			"error" : "Invalid book object in request",
			"helpString" : "Pls pass Data in similar format {'name':'bookname', 'price':5.9, 'isbn':852569}" 
		}
		response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype='application/json')
		return response

Adding Patch Route:

In PATCH method Request body can pass only specific field , that we want to be updated. For example, we can either update the Bookname or the price whichever we want to be updated.

# PATCH /books/isbn

{

‘name’: ‘UpdateNameAlone’

}

or price alone as follows:

# PATCH /books/isbn

{

‘price’: ‘123.45’

}

@app.route('/books/<int:isbn>', methods=['PATCH'])
def update_book(isbn):
	pass #we will define it later

3.6 Defining our PATCH route

Code Snippet_3.6- Defining a PATCH route:

# 3.6 Defining our PATCH route

# PATCH /books/isbn
#{
#	'name': 'UpdateNameAlone'
#}

@app.route('/books/<int:isbn>', methods=['PATCH'])
def update_book(isbn):
	request_data = request.get_json() #we get the JSON data
	updated_book = {} #placeholder for properties to be updated
	if("name" in request_data):
		updated_book["name"] = request_data['name']
	for book in books:
		if book["isbn"] ==isbn:
			book.update(updated_book)
	response = Response("",status=204)
	response.headers['Location'] = "/books/" + str(isbn)
	return response

When we’re coding up this route, we should go the way same as we did before.

3.7 Finishing up our PATCH route

If we want to update price field

# PATCH /books/isbn

{

‘price’: ‘123.45’ #UpdatePriceAlone

}

#Snippet:
	if("price" in request_data):
		updated_book["price"] = request_data['price']

Runnable Code Snippet_3.7-Finishing up our PATCH request :

#app.py
from flask import Flask, jsonify,request,Response

app = Flask(__name__)
print(__name__)

books=[
	{
		'name' : 'Book1',
		'price' : 15.5,
		'isbn' : 123456
	},
	{
		'name' : 'Book2',
		'price' : 12.2,
		'isbn' : 789654
	}
]

#request body
#{
#	'name' : 'Newbook',
#	'price' : 456,
#	'isbn' : 369852
#}

#data sanitizer
def validBookObject(bookObject):
	#if entry already exists
	for i in books:
		if i["isbn"] == bookObject["isbn"]:
			return False
		else:
			#if "keyword" in dictionary	
			if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
				return True
			else:
				return False	

#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
	request_data = request.get_json()#get the req.data by get_json method 
	if(validBookObject(request_data)): #considering only validObject is sent
		new_book = {
			"name": request_data['name'],
			"price": request_data['price'],
			"isbn": request_data['isbn']
		}
		books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
		response = Response("",201,mimetype='application/json')
		response.headers['Location'] = "/books/" + str(new_book['isbn'])
		return response
	else:
		invalidBookObjectErrorMsg = {
			"error" : "Invalid book object in request",
			"helpString" : "Pls pass Data in similar format {'name':'bookname', 'price':5.9, 'isbn':852569}" 
		}
		response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype='application/json')
		return response

#GET/books
@app.route('/books')
def get_all_books():
	return jsonify({'Books':books})

#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
	return_value={}
	for book in books:
		if book["isbn"] == isbn:
			return_value={
				'name' : book["name"],
				'price' : book["price"]
			}
	return jsonify(return_value)


#PUT /books/5869
#{
# 'name': 'NewName',
# 'price': 456	
#}

#PUT route
@app.route('/books/<int:isbn>', methods=['PUT'])
def replace_book(isbn):
	request_data=request.get_json()
	if(not valid_put_request_data(request_data)):
		invalidBookObjectErrorMsg = {
			"error" : "Invalid book object in request",
			"helpString" : "Pls pass Data in following format {'name':'bookname', 'price':5.9, 'isbn':852569}" 
		}
		response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype='application/json')
		return response
	new_book = {
		'name' : request_data['name'],
		'price' : request_data['price'],
		'isbn' : isbn
	}
	i = 0;
	for book in books:
		currentIsbn=book["isbn"]
		if currentIsbn == isbn:
			books[i] = new_book
		i += 1
	response = Response("", status=204) #No content created
	return response
	

# PATCH /books/isbn
#{
#	'name': 'UpdateNameAlone'
#}

# PATCH /books/isbn
#{
#	'price': 'UpdatePriceAlone'	
#}

@app.route('/books/<int:isbn>', methods=['PATCH'])
def update_book(isbn):
	request_data = request.get_json() #we get the JSON data
	updated_book = {} #placeholder for properties to be updated
	if("name" in request_data):
		updated_book["name"] = request_data['name']
	if("price" in request_data):
		updated_book["price"] = request_data['price']
	for book in books:
		if book["isbn"] ==isbn:
			book.update(updated_book)
	response = Response("",status=204)
	response.headers['Location'] = "/books/" + str(isbn)
	return response

app.run(port=5000)

CLI Command and Output:

$ python RSapp.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Postman:

fire a GET request at http://127.0.0.1:5000/books

GET-Requestbody:

#GET-Requestbody
{
    "Books": [
        {
            "isbn": 123456,
            "name": "Book1",
            "price": 15.5
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

fire a PATCH request “name” field alone at http://127.0.0.1:5000/books/123456

PATCH RequestBody (for name field alone):

#PATCH-RequestBody:
{
	"name" : "UpdateNameAlone"
}

PATCH-ResponseBody:

{
    "Books": [
        {
            "isbn": 123456,
            "name": "UpdateNameAlone",
            "price": 15.5
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

#PATCH request “price” field alone at http://127.0.0.1:5000/books/123456

PATCH-RequestBody(for price field alone):

{
	"price" : 6.6
}

ResponseBody:

{
    "Books": [
        {
            "isbn": 123456,
            "name": "Book1",
            "price": 6.6   <==updated field
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

#Status : 204 NO CONTENT (Success but return nothing)

If we hit the Send button , then we also get a 204 no content, and look at the headers, and we can see the location header of the updated resource.

So,this is the end of this module and in this module we started adding code to allow users to replace books already in our bookstore using PUT requests. We then handled a case where a client only wants to update a certain attribute and not have them send all book properties back to us using a PATCH request. In the next module, we will implement the HTTP DELETE method, which will allow clients to delete a book.

For complete Sourcecode visit my Github repo: Link provided in the final module

Rating: 3 out of 5.

RS-codes

3 thoughts on “First time with Flask-Python -Module3

Leave a comment

Design a site like this with WordPress.com
Get started