First time with Flask-Python -Module5

Building my first Flask API

Module 5 :

Storing Data in SQL DB- Python | Flask | API

Welcome back,

This is our fifth module of building our flask API. In the last module, we finished up the DELETE route. So far we restored things locally within our Python server using a books list, and we were not pulling things from a database, therefore whenever we restart the server the net result of this will reset to whatever we hard-coded in our list on the server and all the actions performed such as deletions, additions that were made by client will be reset. This is not an expected way of approach, so in this module, we will change that and we will migrate our book list from being a static list into actually being pulled from a SQL database.

In this module, we will discuss the following:

5.STORING DATA IN SQL DB

5.1 Configuring a Flask SQLAlchemy Database

5.2 Defining Tables and Columns in Flask SQLAlchemy

5.3 Creating the Database

5.4 Defining a Data model with Flask SQLAlchemy

5.5 Updating our Data Model to Query our Database

5.6 Updating our Data Model to Delete Entries from our Database

5.7 Updating our Data Model to Update Table Entries

5.8 Updating our Data Model to Replace Table Entries

5.9 Returning back JSON from our Data Model

5.10 Updating our Flask App to use Our Book Model

5.11 Verifying the Book Model integration

Lets start..

5.STORING DATA IN SQL DB

5.1 Configuring a Flask SQLAlchemy Database

Inorder to do this, we create a ‘settings.py’ file separately to store the settings of our application and configuration of our database. Create an app object .It has a config, and this config takes properties where certain things will be stored. The very first property will be the SQLALCHEMY_DATABASE_URI, and this is the path where we’ll store our database. We’ll be using sqllite, and we set the database path to be within our project directory. i.e., we are going to create a database.db file there. We need to add another config. And this will be the SQLALCHEMY_TRACK_MODIFICATIONS, we set this equal to false. Now these are all the settings that we need, and the main one is the first one where we define the path locally where our database will be stored.

settings.py

#settings.py

from flask import Flask 

#Flask Constructor to create app Object
app = Flask(__name__) #this app Object is shared with RSapp.py file

#configuration to store SQLALCHEMY_DATABASE_URI,this will be the path where we'll store our database

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////home/RSflaskAPI/database.db'   
#Note: My present working directory path
# $pwd: /home/RSflaskAPI(defined the path locally to store DB, inside my project directory)
# you can enter your own project directory path

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


After this we need to create our app.py file, but we already have built it. So just edit it for our specific requirement.

app.py

One thing to note here is, we already have an app object in our settings.py file, so we share it from the settings.py file. Remove this from app.py and rest are same.

#RSapp.py

from settings import *   	#import all from settings.py <=add this

#app = Flask(__name__) 		#since we share the Object from settings.py <=remove this



5.2 Defining Tables and Columns in Flask SQLAlchemy

We need to create a ‘BookModel.py’ file and import SQLAlchemy.

from flask_sqlalchemy import SQLALchemy 

Note: you may need to install sqlalchemy, you can check manually using $pip freeze. Install using $pip install flask_sqlalchemy, if not installed already

then import app Object specifically from settings.py

from settings import app         #importing app Object specifically

Create a database Object

db = SQLAlchemy(app)    #calling SQLAlchemy Constructor and passing it the app Object

Create a class Book, which inherits from db.Model

class Book(db.Model):

Now defined the tablename

__tablename__='books' #this will be the name of the table that adds information to

We already have the DB name defined in our settings.py file, so now we define the Columns. Our books have three properties- name, price and isbn) and for SQL we need to have id as well, so we define 3+1= 4 columns.

id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

SQLAlchemy will map the above variables into actual columns within the SQL DB db.Column from SQLAlchemy. Constructor takes few parameters, 1st parameter is the datatype and other parameters are OPTIONAL.

Code Snippet_5.2-Defining Tables and Columns in Flask SQLAlchemy:

BookModel.py

#BookModel.py

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app      #import app Object specifically from settings.py

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object


#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__='books'

	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

 	#skipped db definition, since its done in settings.py

5.3 Creating the Database

We are going to create our database and our tables and columns.

THIS MUST BE DONE ONLY ONCE, as we create the DB once.

Before creating a DB file check the contents of your current project directory. Showing my project directory content below, Note: we don’t have any DB file inside, as of now.

TERMINAL:

$ ls

BookModel.py  DataSanitizer_TestCase.py  __pycache__  RSapp.py  settings.py

Open up Python. Within here, import our BookModel from BookModel import db. Now that we have this db variable in scope, call the db.create_all method. Once it is executed a database file will be created in our current project directory.

TERMINAL:

$ python
Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from BookModel import db
>>> db.create_all()  #database created
>>> exit()

Check by doing an ls, and you should see that there is a new database.db file here, if you have followed the above steps without any error.

$ ls

BookModel.py  DataSanitizer_TestCase.py  RSapp.py

database.db #<== Newly created database.db file

 __pycache__                settings.py

and now that this database.db file is created, we can see the contents of it by giving cat command, [Note: cat is a Linux operation that you can do to read the contents of a file.] If we do this, you can see that the class that we defined here, and the book, and the tablename.

$ cat database.db

tablename books#CREATE TABLE books (
	id INTEGER NOT NULL, 
	name VARCHAR(80) NOT NULL, 
	price FLOAT NOT NULL, 
	isbn INTEGER, 
	PRIMARY KEY (id)

5.4 Defining a Data model with Flask SQLAlchemy

Define a method called add_book, which takes in a name, a price and an ISBN. Call our book constructor and pass it _name, _price and _isbn this represents what was passed into the function.

def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)

Within our database session, we add this book, and we need to assign the resulting object that’s created into this new book and add it. This theoretically should add a book to the database, but we need a way to actually test to make sure that this works. So, we define another method that will get all the books in our database, then we add a book, and then call the get_all_books method, now we can see the new book that was added. Inherit Book class from the db.Model, it inherits a lot of special functionality, and just utilize it. The db object has a query method, so do our Book class also will have this query method, we can use this to query our database for all the results that we have. Define the book_object, and return a json representation of it where the name will be the current book object’s name, the price will be the current object’s price, and the ISBN will be the current object’s ISBN and return this.

Code Snippet_5.4-Defining a Data model with Flask SQLAlchemy:

#BookModel.py

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()

	def get_all_books():
		return Book.query.all()

	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)

Save this file and open up the terminal and test to make sure that everything is working. First thing is open up Python, then import our BookModel. We’re going to use this API from our app.py file. Call the Book.add_book and then pass a book name, a price and an ISBN number. But we don’t actually know if this worked or not. So to make sure that it worked, call the get_all_books method that we defined. The book we added will now be returned to us.

TERMINAL:

EXPECTED CLI OUTPUT:

$ cd RSflaskAPI

$ ls
BookModel.py  DataSanitizer_TestCase.py  RSapp.py
database.db   __pycache__                settings.py

$ python
Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from BookModel import *

>>> Book.add_book("NewBook",9.9,753357)
>>> Book.get_all_books()
[{"name": "NewBook", "price": 9.9, "isbn": 753357}]


>>> Book.add_book("NewBook2",1.3,432156)
>>> Book.get_all_books()
[{"name": "NewBook", "price": 9.9, "isbn": 753357}, {"name": "NewBook2", "price": 1.3, "isbn": 432156}]
>>> 

5.5 Updating our Data Model to Query our Database

Now lets define the ability to get a book by its ISBN number, for that define a get_book method, which takes in an ISBN passed to it, and then it’ll return the results. Whenever we search for things within our database, we call the Book.query method but this time instead of chaining the query to all, we chain it to a filter_by method which runs a query, we can set it to filter_by isbn and then we pass in the isbn to filter_by, this method takes a parameter and this parameter should match the column that we defined, this query should return only one ISBN, since the ISBNs are unique primary keys. To sum up what we are doin, we used SQLALCHEMY, query method that was inherited from this db.model, call Book.query and then do a bunch of chain commands. We run the query, and then we’re chaining it to what filter we want and then we’re chaining it to only give the first result.

Code Snippet_5.5- Updating our Data Model to Query our Database

# 5.5 Updating our Data Model to Query our Database

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	#Adding books 
	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()

	#get all books	
	def get_all_books():
		return Book.query.all()

	#get book by isbn	
	def get_book(_isbn):
		return Book.query.filter_by(isbn=_isbn).first()

	#output representation	
	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)


From terminal, call Python and import our BookModel and get all the books in it, now get any book’s ISBN from the list and pass it, we should get the corresponding book now, if you have followed the above steps.

TERMINAL:

#EXPECTED OUTPUT:

$ python
Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from BookModel import *

>>> Book.get_all_books()
[{"name": "NewBook1", "price": 9.9, "isbn": 753357}, {"name": "NewBook2", "price": 1.3, "isbn": 432156}]


>>> Book.get_book(432156)
{"name": "NewBook2", "price": 1.3, "isbn": 432156}


>>> Book.get_book(753357)
{"name": "NewBook1", "price": 9.9, "isbn": 753357}
>>> 



5.6 Updating our Data Model to Delete Entries from our Database

The delete_book method is similar to the get_book method that we wrote previously. Lets code it, define a method called delete_book which takes an ISBN we pass in. Run the Book.query method and filter_by isbn, same as we did above but here instead of running get the first result, we chain to the delete method it deletes all books that matches the filter. After we delete it, we need to save this changes by calling the db.session .commit that we had before.

Code Snippet_5.6- Updating our Data Model to Delete Entries from our Database

# 5.6 Updating our Data Model to Delete Entries from our Database

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	#Adding books 
	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()  #to save the changes

	#get all books	
	def get_all_books():
		return Book.query.all()

	#get book by isbn	
	def get_book(_isbn):
		return Book.query.filter_by(isbn=_isbn).first()

	#delete book by isbn	
	def delete_book(_isbn):
		Book.query.filter_by(isbn=_isbn).delete()
		db.session.commit() #To save the changes	

	#output representation	
	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)

From terminal open up Python and then import our BookModel, now get all the books to see which one we want to delete. Call the Book.delete_book method and pass in any ISBN from the list. To check whether it has deleted, get all the books again.Now we must see the specific book removed.

TERMINAL:

#EXPECTED OUTPUT:

$ python
Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from BookModel import *

>>> Book.get_all_books()
[{"name": "NewBook1", "price": 9.9, "isbn": 753357}, {"name": "NewBook2", "price": 1.3, "isbn": 432156}]


>>> Book.delete_book(432156)

>>> Book.get_all_books()
[{"name": "NewBook1", "price": 9.9, "isbn": 753357}]
>>>

5.7 Updating our Data Model to Update Table Entries

Define update_book_price method which takes an ISBN of the book to update, and its price. The book_to_update method runs the same Book.query .filter_by with the passed in ISBN. To update, use dot notation to add a property that we want to update, then the value of the property and commit the change. To update the book’s name, define update_book_name and follow the same steps as above, instead of taking a price, it’ll take in a name, and it’ll update the name property here instead of the price property.

Code Snippet_5.7- Updating our Data Model to Update Table Entries:

# 5.7 Updating our Data Model to Update Table Entries

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	#Adding books 
	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()  #to save the changes

	#get all books	
	def get_all_books():
		return Book.query.all()

	#get book by isbn	
	def get_book(_isbn):
		return Book.query.filter_by(isbn=_isbn).first()

	#delete book by isbn	
	def delete_book(_isbn):
		Book.query.filter_by(isbn=_isbn).delete()
		db.session.commit() #To save the changes	

	#to update a property (using Dot notation)	
	def update_book_name(_isbn, _name):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.name = _name #using Dot notation,update property 'name'
		db.session.commit() #save changes

	def update_book_price(_isbn, _price):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.price = _price
		db.session.commit()	#save changes


	#output representation	
	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)

		

Open up terminal and test to make sure everything is working as expected. Open Python and import everything from our BookModel and get all the books in our collection. Now call the Book.update_book_price API, and the first parameter it takes is the ISBN. Change the price property to any value. Once the updation is done, do the Book.get_all_books, Now you can see the updated price, as expected. Check the update book name method, same as above.

TERMINAL:

#EXPECTED OUTPUT:

$ python
Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.


>>> from BookModel import *

>>> Book.get_all_books()
[{"name": "NewBook", "price": 9.9, "isbn": 753357}]

>>> Book.update_book_name(753357,"Book1")

>>> Book.get_all_books()
[{"name": "Book1", "price": 9.9, "isbn": 753357}]



>>> Book.update_book_price(753357,4.4)

>>> Book.get_all_books()
[{"name": "Book1", "price": 4.4, "isbn": 753357}]
>>>


5.8 Updating our Data Model to Replace Table Entries

Define a method replace_book, which takes an ISBN, a name and a price. We need to find the book to replace, define the method to run query to filter by ISBN. And now we need to update both properties instead of one through book_to_replace. price and book_to_replace.name which replaces the price and name passed in. Finally we need to run the db.session .commit, which will commit our changes to the database.

Code Snippet_5.8-Updating our Data Model to Replace Table Entries

# 5.8 Updating our Data Model to Replace Table Entries

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	#Adding books 
	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()  #to save the changes

	#get all books	
	def get_all_books():
		return Book.query.all()

	#get book by isbn	
	def get_book(_isbn):
		return Book.query.filter_by(isbn=_isbn).first()

	#delete book by isbn	
	def delete_book(_isbn):
		Book.query.filter_by(isbn=_isbn).delete()
		db.session.commit() #To save the changes	

	#to update a property (using Dot notation)	
	def update_book_name(_isbn, _name):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.name = _name #using Dot notation,update property 'name'
		db.session.commit() #save changes

	def update_book_price(_isbn, _price):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.price = _price
		db.session.commit()	#save changes

	#to replace Table Entries
	def replace_book(_isbn, _name, _price):
		book_to_replace = Book.query.filter_by(isbn=_isbn).first()
		book_to_replace.name = _name
		book_to_replace.price = _price
		db.session.commit()	#save changes

	#output representation	
	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)



Once the coding is done, open a terminal and open up Python. From the BookModel import everything. Now get all the books, to replace a book call the Book.replace_book method, pass it an ISBN, a name and price parameters. After running this call the Book.get_all_books method, now we can see the ISBN is the same but all the parameters name and price are replaced.

TERMINAL:

#EXPECTED OUTPUT:

$ python

Python 3.6.8 (default) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.


>>> from BookModel import *
>>> Book.get_all_books()
[{"name": "Book1", "price": 4.4, "isbn": 753357}]


>>> Book.replace_book(753357,"NewBook",10)
>>> Book.get_all_books()
[{"name": "NewBook", "price": 10.0, "isbn": 753357}]
>>> 



5.9 Returning back JSON from our Data Model

We need to get a JSON representation of the data we are getting back from the above queries. Since SQLALCHEMY objects returned are not easy for us to interpret. Our book has a name field, price field and ISBN field which return self.name, self.price and self.isbn respectively. Change these queries, so instead of returning SQLAlchemy objects we’ll call our JSON method on it so that we can get JSON that we can use in our app.py file.

Our Book.query.all method returns book objects which is incompatible with our Flask application. Since, we need JSON back. For each book in our book query, we have to return the JSON representation of that. It’s going to loop through all the books that’s returned with the query, and then for each book we are going to call our json method, and this will be returned to us as a complete list in more human readable format

Code Snippet_5.9-Returning back JSON from our Data Model

# 5.9 Returning back JSON from our Data Model

#BookModel.py

#Snippet:

# __repr__ method is useful only in Terminal
	#so we define a new method for json output
	def json(self):
		return{'name': self.name, 'price':self.price, 'isbn': self.isbn}


#get all books	
	def get_all_books():
		return [Book.json(book) for book in Book.query.all()]
#Note we have returned the json format



5.10 Updating our Flask App to use Our Book Model – migrating all from BookModel and settings.py to app.py

We are going to use the BookModel that we created and migrate all of that into our app.py file. From the BookModel , and settings import everything.

Note 1: our settings.py has app object in it, so we can remove those code from app.py

Note 2: We don’t need the array of books to be defined in app.py, as we will be pulling them from our database. Rest of the edits are given in the code snippet accompanied with comments.

Code Snippet_5.10a- Updating our Flask App to use Our Book Model – app.py

# 5.10a- Updating our Flask App to use Our Book Model

# migrating all from BookModel and settings.py to app.py

#RSapp.py

from flask import Flask, jsonify,request,Response

from BookModel import *
from settings import *

import json   	#import Json
from settings import *   #import all

#app = Flask(__name__) #removed since we share the Object from settings.py

"""
#since we will be pulling the books list from database,
#we dont need it defined here anymore

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


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

 #Sanitize the data received from Client
#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
		Book.add_book(request_data['name'], request_data['price'], request_data['isbn'])
		response = Response("", status=201, mimetype='application/json')
		response.headers['Location'] = "/books" + str(request_data['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':Book.get_all_books()}) #from database

#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
	return_value=Book.get_by_isbn(isbn)
	return jsonify(return_value)

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


def valid_put_request_data(request_data):
	if ("name" in request_data 
		and "price" in request_data):
		return True
	else:
		return False

#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

	Book.replace_book(isbn, request_data['name'],request_data['price'])	
	response = Response("", status=204) #No content created
	return response

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

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


def valid_patch_request_data(request_data):
	if ("name" in request_data 
		or "price" in request_data):
		return True
	else:
		return False

@app.route('/books/<int:isbn>', methods=['PATCH'])
def update_book(isbn):
	request_data = request.get_json() #we get the JSON data
	if(not valid_patch_request_data(request_data)):
		invalidBookObjectErrorMsg = {
			"error": "Invalid book object passed in request",
			"helpstring": "Data should be in following format{'name': 'bookname', 'price':15}"
		}
		response = Response (json.dumps(invalidBookObjectErrorMsg),staus=404,mimetype='application/json')
		return response
	
	if("name" in request_data):
		Book.update_book_name(isbn, request_data['name'])
	if("price" in request_data):
		Book.update_book_price(isbn, request_data['price'])
	
	response = Response("",status=204) #204=Success
	response.headers['Location'] = "/books/" + str(isbn)
	return response

#DELETE /books/isbn
@app.route('/books/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
	if(Book.delete_book(isbn)):
		response = Response("",status=204)
		return response

	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)

Code Snippet_5.10b- Updating our Flask App to use Our Book Model – BookModel.py

# 5.10b- Updating our Flask App to use Our Book Model

#BookModel.py

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
import json
from settings import app #import app Object specifically 

#creating a database Object
db = SQLAlchemy(app)  #calling SQLAlchemy Constructor and passing it the app Object

#class Book inherit from db.Model
class Book(db.Model):
	#define tablename 
	__tablename__ = 'books'
	#define columns 
	id = db.Column(db.Integer, primary_key=True)
	name = db.Column(db.String(80), nullable=False) #cant be NULL
	price = db.Column(db.Float, nullable=False)
	isbn = db.Column(db.Integer)

	# __repr__ method is useful only in Terminal
	#so we define a new method for json output
	def json(self):
		return{'name': self.name, 'price':self.price, 'isbn': self.isbn}

	#Adding books 
	def add_book(_name, _price, _isbn):
		new_book=Book(name=_name, price=_price, isbn=_isbn)
		db.session.add(new_book)
		db.session.commit()  #to save the changes

	#get all books	
	def get_all_books():
		return [Book.json(book) for book in Book.query.all()]

	#get book by isbn	
	def get_book(_isbn):
		return Book.json(Book.query.filter_by(isbn=_isbn).first())

	#delete book by isbn	
	def delete_book(_isbn):
		is_successful = Book.query.filter_by(isbn=_isbn).delete()
		db.session.commit() #To save the changes
		return bool(is_successful)	

	#to update a property (using Dot notation)	
	def update_book_name(_isbn, _name):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.name = _name #using Dot notation,update property 'name'
		db.session.commit() #save changes

	def update_book_price(_isbn, _price):
		book_to_update = Book.query.filter_by(isbn=_isbn).first()
		book_to_update.price = _price
		db.session.commit()	#save changes

	#to replace Table Entries
	def replace_book(_isbn, _name, _price):
		book_to_replace = Book.query.filter_by(isbn=_isbn).first()
		book_to_replace.name = _name
		book_to_replace.price = _price
		db.session.commit()	

	#output representation	
	def __repr__(self):
		book_object={
			'name': self.name,
			'price': self.price,
			'isbn': self.isbn
		}
		return json.dumps(book_object)

Code Snippet_5.10c- Updating our Flask App to use Our Book Model – settings.py

#5.10c- Updating our Flask App to use Our Book Model
#settings.py

from flask import Flask 


#Flask Constructor to create app Object
app = Flask(__name__) #this app Object is shared with RSapp.py file

#config to store SQLALCHEMY_DATABASE_URI,
#this will be the path where we'll store database

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////home/RSflaskAPI/database.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# my project directory path: /home/RSflaskAPI/database.db


5.11 Verifying the Book Model integration


Keep our Server running, drag over Postman to test different routes and make sure that everything is working. Do a GET request, we can see the books in our book collection. To run the delete route, pass it ISBN for the book you want to delete. You will get a 204 NO CONTENT, which is the correct success message. To check send the GET request again and notice that the specific book removed from list. To check PATCH request for a specific ISBN, (set the correct content type headers, and for the body change the price of the book or the name)now send the PATCH request and get a 204 NO CONTENT, which means that was success. To check it make a GET request again and notice the book gets updated.

To make sure that these changes are persistent, restart the server and give a GET request, all the changes made should be there, as expected if everything works correctly.

TERMINAL:

From Postman fire GET, POST, PUT, PATCH and DELETE requests -http methods

$ python RSapp.py

 * Serving Flask app "settings" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

127.0.0.1 - - [] "GET /books HTTP/1.1" 200 - #notice the status codes
127.0.0.1 - - [] "POST /books HTTP/1.1" 201 -
127.0.0.1 - - [] "GET /books HTTP/1.1" 200 -
127.0.0.1 - - [] "PATCH /books/123321 HTTP/1.1" 204 -
127.0.0.1 - - [] "GET /books HTTP/1.1" 200 -
127.0.0.1 - - [] "PUT /books/753357 HTTP/1.1" 204 -
127.0.0.1 - - [] "GET /books HTTP/1.1" 200 -
127.0.0.1 - - [] "DELETE /books/123321 HTTP/1.1" 204 -
127.0.0.1 - - [] "GET /books HTTP/1.1" 200 -
^C  # to stop
$ 

By this we reached the end of this module,here we migrated our book list from being static into actually using a SQL database. We built out a book model that connected to our database, and then we migrated our REST API endpoints to use our book model instead of being hard-coded. In the next module, we will add authentication to our API. Stay updated..

love,

<R.S>

Complete Sourcecode available in my Github repo: Link provided in the final module.

Rating: 5 out of 5.

RS-codes

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

Leave a comment

Design a site like this with WordPress.com
Get started