First time with Flask-Python -Module4

Building my first Flask API

Module 4 :

Python | Flask API – DELETING DATA

Welcome back,

This is our fourth module of building our flask API. In the last module, we finished implementing the PUT and PATCH HTTP methods, which allowed clients to update books that were already created in the store. And in this module we’ll implement the DELETE method.

In this module, we will discuss the following:

4.DELETING DATA

4.1 Adding a DELETE route

4.2 Coding our DELETE route

4.3 Sending DELETE requests to our Flask App

4.4 Finishing up our DELETE route

By the end of this module, you will be able to do delete books already in our bookstore using DELETE requests, and a clear view of what status codes and responses we should be sending for various scenarios.

Lets start coding…

4.DELETING DATA (HTTP DELETE method)

4.1 Adding a DELETE route

The very first thing we need to do is to add a new route. This route will point to the same endpoint as our book’s ISBN route that allowed a user to get a single book from our store. Within this method we need to pass in DELETE, so the route knows which method to be called on, and then we’ll define a function delete_book, and it will take in that ISBN, and for now we’ll put in a pass.

Code Snippet_4.1- Adding a DELETE route:

#DELETE /books/isbn
@app.route('/books/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
	pass

This route should look very similar to the previous routes where we had clients either update a book in this route, add a book to this route, patch a book, replace a book, etc.,

Save and Run from CLI, to check the code is working without any error. We are yet to define the method.

4.2 Coding our DELETE route

Now we are going to allow our client to send a DELETE request to a book with a given ISBN number. In the DELETE request, all we need to do is go through all the books in our collection, find one that matches the ISBN number that was passed in, and once we find a match, then we remove it from our store. (even if there are duplicate ISBN numbers, they all will be deleted). Our client will send us a DELETE request, and for the body they actually don’t need to send us anything. Unlike the previous route which we built where we actually need to use this request.get_json method, in this route we don’t actually need to do that because it doesn’t matter what body our clients send us.

Code Snippet_4.2- Coding our DELETE route

#DELETE /books/isbn
@app.route('/books/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
	for book in books:
		if book["isbn"] == isbn:
			return jsonify(book)  #<==juz to check

This return book statement is just to check whether our route is set up correctly and our server is able to receive DELETE request. Now add the snippet ,save and run the Server up from the terminal and open Postman to fire request.

Terminal:

Keep the server up

Postman:

GET Request:

#Expected Output for the above GET request:
{
    "Books": [
        {
            "isbn": 123456,
            "name": "Book1",
            "price": 15.5
        },
        {
            "isbn": 789654,
            "name": "Book2",
            "price": 12.2
        }
    ]
}

Fire a DELETE request from Postman:

DELETE req at http://127.0.0.1:5000/books/789654

Response Body:

#Response Body:
{
    "isbn": 789654,
    "name": "Book2",
    "price": 12.2
}

When we do a DELETE request to a ISBN, we should get the actual JSON body for that book’s ISBN, which would give us the name, the price, and the ISBN of it. We can make sure our DELETE route is successfully connected, if we get back the book data of the ISBN we passed into the URL.

4.3 Sending DELETE requests to our Flask App

We define a counter here. This counter is used to index which book we want to delete from our book collection. We use the lists pop method, and this takes an integer, which is the index in the list for whatever book we want to remove. For all the books in our book collection, if we have a book ISBN that matches the ISBN passed in, it’ll pop, which means it’ll remove that index from the total collection of our list. Because we need to make this counter instead of always being hard-coded to 0 actually increment each time a book is iterated through in our total collection. Now for every book in our books collection, we will add one to this counter, so by the end this will reach the total size of the book collection. For now we are going to return an empty string and we will test to make sure that this whole delete books is working.

Code Snippet_4.3- Sending DELETE request to our Flask App

#DELETE /books/isbn
@app.route('/books/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
	i = 0 #counter 'i' , used to index which book we want to delete
	for book in books:
		if book["isbn"] == isbn:
			books.pop(i) #removes that indexed content from List
		i += 1 #increment it each time a book is iterated thru
	return "" #empty string

Save and open up the terminal and then restart our server, as we did earlier. From Postman, first we do a GET request to make sure we can get the books, and then we try to delete first book from our collection. When we do this DELETE method and then we do a GET, we should not see the book that we deleted, anymore.

Terminal:

Restart the server up

Postman:

First give a GET request, as we did in our previous discussions.

#Expected Output after GET request

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

Now give a DELETE request at http://127.0.0.1:5000/books/789654

Status 200 OK
Empty body   #returns nothing, since empty string

We get a 200 OK with an empty body, but this doesn’t necessarily mean that this is working, to make sure that things are working as expected. We give GET request again.

Giving a GET request again, now we should not see the book that we deleted.

#Expected output after giving GET request ,after Deleting a entry through DELETE req
{
    "Books": [
        {
            "isbn": 123456,
            "name": "Book1",
            "price": 15.5
        }
    ]
}

As you can see, everything is working as expected. Now the next thing we need to do is send back the correct response headers and status codes to our client, since we haven’t explicitly set those yet.

4.4 Finishing up our DELETE route

To finish up our DELETE route we add status code and response Location header. If we reach a case in which a Book with the ISBN number that was provided was not found. So therefore, unable to delete. That’s an error, so we need to return this invalid book object error message, and then we do json.dump it. That way it becomes a JSON object. And then we return the response back to the client. And the status code should be a 404(resource not found) because the resource wasn’t found because the only way a book cannot be deleted is if the ISBN does not match what we have in our current collection. Then we put the mimetype as application/json.

Runnable Code Snippet_4.4- Finishing up our DELETE route

# 4.4 Finishing up our DELETE route

from flask import Flask, jsonify,request,Response,json 

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 similar 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' 
#}

#PATCH route
@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) #204=Success
    response.headers['Location'] = "/books/" + str(isbn)
    return response

#DELETE route
#DELETE /books/isbn
@app.route('/books/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
    i = 0; #counter 'i' , used to index which book we want to delete
    for book in books:
        if book["isbn"] == isbn:
            books.pop(i) #removes that indexed content from List
            response = Response("",status=204) #204=Success
            return response
        i += 1 #increment it each time a book is iterated thru
    invalidBookObjectErrorMsg = {
        "error":"Unable to Delete,as ISBN provided doesnot found in the List"
    }
    response = Response(json.dumps(invalidBookObjectErrorMsg),status=404, mimetype='application/json') #Status404:Resource not found    
    return response

app.run(port=5000)

Terminal:

Restart the server up

Postman:

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

#Expected Output for the above GET request:

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

Lets check, how well it handles certain condition, say by giving an invalid ISBN in the DELETE request.

Now fire DELETE req at http://127.0.0.1:5000/books/7854

Response Body:

#Expected Output:

#Response Body:
{
    "error": "Unable to Delete,as ISBN provided doesnot found in the List"
}

#Status: 404 NOT FOUND

By this we come to the end of this module. In this module, we started allowing code to allow users to delete books already in our bookstore using PUT requests. We then discussed what status codes and responses we should be sending for various scenarios. In the next module, we migrate our books list from being a static list into actually being pooled in the SQL database.

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

Rating: 4 out of 5.

RS-codes

2 thoughts on “First time with Flask-Python -Module4

Leave a comment

Design a site like this with WordPress.com
Get started