You might heared about singleton set in Mathematics, sometimes called as Unit set, its nature is that it conatins only a single element in its set.
For example:- take a look at the set {null}, it conatins only the element null in its set so it can be termed as a singleton.
And also Singleton set is the only set in category of sets to be a ‘Terminal Object’. Terminal Object in sense can be mentioned as ‘Final’.
You know, why we call it a Final ?
It because a set A{X} has ‘X’ as its single element which is mapped to its set ‘A’, lets pass set A to a function,
f(A)=>X
and you see that the function returns a single element X.
Singleton has an intresting property which is that if you make a function out of it corresponding to any arbitary or random set, you may find it holds Injective to the elements of arbitary set,Injective in sense we can understand that the single element of Singleton set holds a one to one relationship with elemets of arbitary sets.
Note: Only an empty set {empty} has this injective property other than the singleton set , you know why we don’t term empty set as Singleton , because it has a cardinality of 0 while Singleton requires a cardinality of 1.
Does this Singleton helps anyway in designing software solutions ?
The answer is YES.
Why?
It’s because in Object Oriented Software Design there is a class structure called Singleton Class introduced by the Gang of Four in their book Design Patterns where the Singleton class actualy can be instanciated only once .
While the singleton program itself maitains the class to be instanciated only once which means it contains a single object and multiple objects of the same class is prohibted.
This pattern restricts the instantiation of a class to one object.
Singleton class implementation in python
singleton.py
class Singleton: __instance = None
@staticmethod def getInstance():
""" Static access method. """
if Singleton.__instance == None:
Singleton()
return Singleton.__instance def __init__(self):
""" Virtually private constructor. """
if Singleton.__instance != None:
raise Exception("This class is a singleton!")
else:
Singleton.__instance = selfs = Singleton()
print (s)
s = Singleton.getInstance()
print (s)
s = Singleton.getInstance()
print (s)
Output:
<__main__.Singleton object at 0x7f4ca69cfca0>
<__main__.Singleton object at 0x7f4ca69cfca0>
<__main__.Singleton object at 0x7f4ca69cfca0>
When you make a class as Singleton then the property of Singleton Set will be applicable here as well. One of them is the Injective property, where we map our class instance in one to one relationship to its consumers.
In Angular Framework thers is a concept called Dependency Injection which works same as Singleton, Angular calls them Services which can be Injected to any component , and that component will be the root component which shares the instance of the service class to all its child components. And the child components can construct the service class in their class and make use of the methods and variables provided by the service class.
Imagine you are giving a seminar in your class then you are the service and everybody in the room are your consumers, every audience in the room hears exactly the same speech you present. Which means that One instance of you mapped to all your audience.
However it kind of bad while desinging a service that fetches the database instance and shares it with a group of functions which adds,edits or deletes tables in a database. Most of the people while designing an api service will do this mistake.
If you see the code down here, you can understand that the db variable holds single Sqlalchemy orm instance of the flask app which is shared by all the consumer classes which consists of database model class and rest api functions.
from flask import Flask, request, flash, url_for, redirect, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.sqlite3'
app.config['SECRET_KEY'] = "random string"
db = SQLAlchemy(app) // Single Instance of Database
class students(db.Model):
id = db.Column('student_id', db.Integer, primary_key = True)
name = db.Column(db.String(100))
city = db.Column(db.String(50))
addr = db.Column(db.String(200))
pin = db.Column(db.String(10))
def __init__(self, name, city, addr,pin):
self.name = name
self.city = city
self.addr = addr
self.pin = pin
@app.route('/')
def show_all():
return render_template('show_all.html', students = students.query.all() )
@app.route('/new', methods = ['GET', 'POST'])
def new():
if request.method == 'POST':
if not request.form['name'] or not request.form['city'] or not request.form['addr']:
flash('Please enter all the fields', 'error')
else:
student = students(request.form['name'], request.form['city'],
request.form['addr'], request.form['pin'])
db.session.add(student)
db.session.commit()
flash('Record was successfully added')
return redirect(url_for('show_all'))
return render_template('new.html')@app.route('/edit/<id>', methods = ['GET', 'POST', 'PUT'])
def edit():
if request.method == 'PUT':
if not request.form['name'] or not request.form['city'] or not drequest.form['addr']:
flash('Please enter all the fields', 'error')
else:
student = students(request.form['name'], request.form['city'],
request.form['addr'], request.form['pin']).query.filter(id=id)
db.session.add(student)
db.session.commit()
flash('Record was successfully edited')
return redirect(url_for('show_all'))
return render_template('new.html')@app.route('/delete/<id>', methods = ['DELETE'])
def delete():
if request.method == 'DELETE':
student = students.query.filter(id=id)db.session.delete(student)
db.session.commit()
flash('Record was successfully deleted')
return redirect(url_for('show_all'))
return render_template('new.html')
if __name__ == '__main__':
db.create_all()
app.run(debug = True
// source code credits: Tutorialspoint
// i have added the edit and delete operation to this code as the // orginal example didn't had this.
Why the single instance sharing is a bad practice (when sharing db session) here in the code above ?
Because as you see that the Sqlalchemy orm instance of the flask app has been shared by show_all( ) , new( ) , edit( ) and delete( ) functions.
When they try to access db session then they get single db session as well.
If a single user comes in to your website he may choose either the new( ), edit( ), delete( ) or show_all( )
But what if multiple users come in and access all the fuctions at once ? !!
Do you think the session gets commited ?
No way… why? as you see there is this single Sqlalchemy orm instance of the flask app shared among all the users accesing various functions, then when everyone tries to commit, the db session will not be available to anyone, thus rollback happens.
If the code contains a rollback during an exception and the session never gets commited.
So in this case we need a local db session for every function of the api service.
So, the solution should be like this for every http method operations
db = SQLAlchemy(app) // global instance
@app.route('/new', methods = ['GET', 'POST'])
def new():
if request.method == 'POST': local_Session_Of_Db = db.session() // local db session
if not request.form['name'] or not request.form['city'] or not request.form['addr']:
flash('Please enter all the fields', 'error')
else:
student = students(request.form['name'], request.form['city'],
request.form['addr'], request.form['pin'])
local_Session_Of_Db.add(student)
local_Session_Of_Db.commit()
flash('Record was successfully added')
return redirect(url_for('show_all'))
return render_template('new.html')
Still Sqlalchemy orm instance of the flask app is a singleton yet i maintain it as a global reference while getting new db session method locally to each functions seperately.
You see the Flask app class is injected to our SQLAlchemy class while SQLAlchemy class is instanciated and shared with our consumer functions called the add,edit,delete and show_all operations.
Its a colabarative way of making use of the singleton service without affecting the commit cycle of the database as multiple users hits our api, as api itself a singleton which is a single entry point to all its users.
Ya, can you get it, the api service as a whole serves everyone with single endpoint for fetching all records, add, edit and delete records opearations.
But how, for example the url ‘/new’ is common for everyone who wants to add a new record to the student table.
All the users who are using the api add operation are mapped to the ‘/new’ url.
As whoever hits the api with HTTP payload using the api url ‘/new’ can create a new student record in table as the service for adding a new record in sense is a singleton.
Visit My Personal Blog @ danyson.github.io