Model and Object-Oriented Programming
Object Oriented Programming is a part of learning Python. The objective of this blog is to introduce OOP with the intention of PBL task to create a database. The foundations for a database is defining a Class and understanding instance data and methods. A database is often a focus of backend coding as it will store persistent data, that can be recalled after the immediate session is closed.
Class and Object Terms
The foundations of Object-Oriented Programming is defining a Class
In Object-Oriented Programming (OOP), a class is a blueprint for creating an Object. (a data structure). An Object is used like many other Python variables. A Class has ...* a collection of data, these are called ***Attributes*** and in Python are pre-fixed using the keyword ***self*** * a collection of Functions/Procedures. These are called ***Methods** when they exist inside a Class definition.
An Object is created from the Class/Template. Characteristics of objects ... an Object is an Instance of the Class/Template there can be many Objects created from the same Class each Object contains its own Instance Data the data is setup by the Constructor, this is the "init" method in a Python class all methods in the Class/Template become part of the Object, methods are accessed using dot notation (object.method()) A Python Class allow for the definition of @ decorators, these allow access to instance data without the use of functions ...
* @property decorator (aka getter). This enables developers to reference/get instance data in a shorthand fashion (object.name versus object.get_name()) * @name.setter decorator (aka setter). This enables developers to update/set instance data in a shorthand fashion (object.name = "John" versus object.set_name("John")) * observe all instance data (self._name, self._email ...) are prefixed with "_", this convention allows setters and getters to work with more natural variable name (name, email ...)
# Werkzeug is a collection of libraries that can be used to create a WSGI (Web Server Gateway Interface)
# A gateway in necessary as a web server cannot communicate directly with Python.
# In this case, imports are focused on generating hash code to protect passwords.
from werkzeug.security import generate_password_hash, check_password_hash
import json
# Define a User Class/Template
# -- A User represents the data we want to manage
class User:
# constructor of a User object, initializes the instance variables within object (self)
def __init__(self, name, uid, password):
self._name = name # variables with self prefix become part of the object,
self._uid = uid
self.set_password(password)
# a name getter method, extracts name from object
@property
def name(self):
return self._name
# a setter function, allows name to be updated after initial object creation
@name.setter
def name(self, name):
self._name = name
# a getter method, extracts email from object
@property
def uid(self):
return self._uid
# a setter function, allows name to be updated after initial object creation
@uid.setter
def uid(self, uid):
self._uid = uid
# check if uid parameter matches user id in object, return boolean
def is_uid(self, uid):
return self._uid == uid
@property
def password(self):
return self._password[0:10] + "..." # because of security only show 1st characters
# update password, this is conventional setter
def set_password(self, password):
"""Create a hashed password."""
self._password = generate_password_hash(password, method='sha256')
# check password parameter versus stored/encrypted password
def is_password(self, password):
"""Check against hashed password."""
result = check_password_hash(self._password, password)
return result
# output content using str(object) in human readable form, uses getter
def __str__(self):
return f'name: "{self.name}", id: "{self.uid}", psw: "{self.password}"'
# output command to recreate the object, uses attribute directly
def __repr__(self):
return f'Person(name={self._name}, uid={self._uid}, password={self._password})'
# tester method to print users
def tester(users, uid, psw):
result = None
for user in users:
# test for match in database
if user.uid == uid and user.is_password(psw): # check for match
print("* ", end="")
result = user
# print using __str__ method
print(str(user))
return result
# place tester code inside of special if! This allows include without tester running
if __name__ == "__main__":
# define user objects
u1 = User(name='Thomas Edison', uid='toby', password='123toby')
u2 = User(name='Nicholas Tesla', uid='nick', password='123nick')
u3 = User(name='Alexander Graham Bell', uid='lex', password='123lex')
u4 = User(name='Eli Whitney', uid='eli', password='123eli')
u5 = User(name='Hedy Lemarr', uid='hedy', password='123hedy')
# put user objects in list for convenience
users = [u1, u2, u3, u4, u5]
# Find user
print("Test 1, find user 3")
u = tester(users, u3.uid, "123lex")
# Change user
print("Test 2, change user 3")
u.name = "John Mortensen"
u.uid = "jm1021"
u.set_password("123qwerty")
u = tester(users, u.uid, "123qwerty")
# Make dictionary
'''
The __dict__ in Python represents a dictionary or any mapping object that is used to store the attributes of the object.
Every object in Python has an attribute that is denoted by __dict__.
Use the json.dumps() method to convert the list of Users to a JSON string.
'''
print("Test 3, make a dictionary")
json_string = json.dumps([user.__dict__ for user in users])
print(json_string)
print("Test 4, make a dictionary")
json_string = json.dumps([vars(user) for user in users])
print(json_string)
Test 1, find user 3 name: "Thomas Edison", id: "toby", psw: "sha256$NZF..." name: "Nicholas Tesla", id: "nick", psw: "sha256$LLf..."
- name: "Alexander Graham Bell", id: "lex", psw: "sha256$iOB..." name: "Eli Whitney", id: "eli", psw: "sha256$5W1..." name: "Hedy Lemarr", id: "hedy", psw: "sha256$Bpx..." Test 2, change user 3 name: "Thomas Edison", id: "toby", psw: "sha256$NZF..." name: "Nicholas Tesla", id: "nick", psw: "sha256$LLf..."
- name: "John Mortensen", id: "jm1021", psw: "sha256$pwK..." name: "Eli Whitney", id: "eli", psw: "sha256$5W1..." name: "Hedy Lemarr", id: "hedy", psw: "sha256$Bpx..." Test 3, make a dictionary [{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$NZFZo9MgcZWiswEb$8f2400031d5a74a02a3abe23215554ff6f7b4c7e15a761587e7e7105f9c370e1"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$LLfWY1czmI0nCvIB$20a8a60bc24991c30038ae9976c66b3764193ade9b2f39561a7dace15bce573b"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$pwKmPHOZSTk5BRmH$17766eb3162740ba01745dbc7a33c2163ddfe6f872089ca461fc11dfd9538882"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$5W11yn1jwsPNXkbO$a8f8c882f56d42ced7c33b7ce99c692387f8553013795b082e9f8f7480f1fa67"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$BpxCYqFfdLBGPWGg$c6cc58469c7804c67e9ee87330ce273feee2737805348fd3ca51326013e4e710"}] Test 4, make a dictionary [{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$NZFZo9MgcZWiswEb$8f2400031d5a74a02a3abe23215554ff6f7b4c7e15a761587e7e7105f9c370e1"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$LLfWY1czmI0nCvIB$20a8a60bc24991c30038ae9976c66b3764193ade9b2f39561a7dace15bce573b"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$pwKmPHOZSTk5BRmH$17766eb3162740ba01745dbc7a33c2163ddfe6f872089ca461fc11dfd9538882"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$5W11yn1jwsPNXkbO$a8f8c882f56d42ced7c33b7ce99c692387f8553013795b082e9f8f7480f1fa67"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$BpxCYqFfdLBGPWGg$c6cc58469c7804c67e9ee87330ce273feee2737805348fd3ca51326013e4e710"}]
Hacks
Add new attributes/variables to the Class. Make class specific to your CPT work.
- Add classOf attribute to define year of graduation
- Add setter and getter for classOf
- Add dob attribute to define date of birth
- This will require investigation into Python datetime objects as shown in example code below
- Add setter and getter for dob
- Add instance variable for age, make sure if dob changes age changes
- Add getter for age, but don't add/allow setter for age
- Update and format tester function to work with changes
Start a class design for each of your own Full Stack CPT sections of your project
- Use new
code cell
in this notebook- Define init and self attributes
- Define setters and getters
- Make a tester
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import date
import json
class User:
def __init__(self, name, uid, password, dob, classOf, stockNames):
self._name = name # variables with self prefix become part of the object,
self._uid = uid
self.set_password(password)
self._dob = dob
self._classOf = classOf
self._stockNames = stockNames
@property
def name(self):
return self._name
# a setter function, allows name to be updated after initial object creation
@name.setter
def name(self, name):
self._name = name
# a getter method, extracts email from object
@property
def uid(self):
return self._uid
# a setter function, allows name to be updated after initial object creation
@uid.setter
def uid(self, uid):
self._uid = uid
# check if uid parameter matches user id in object, return boolean
def is_uid(self, uid):
return self._uid == uid
# dob property is returned as string, to avoid unfriendly outcomes
@property
def dob(self):
dob_string = self._dob.strftime('%m-%d-%Y')
return dob_string
# dob should be have verification for type date
@dob.setter
def dob(self, dob):
if type(dob)== date:
self._dob = dob
# Type returns type of an object
# If type is a date we are changing it
@property
def classOf(self):
return self._classOf
@classOf.setter
def classOf(self, classOf):
self._classOf = classOf
@property
def stockNames(self):
return self._stockNames
@stockNames.setter
def stockNames(self, stockNames):
self._stockNames = stockNames
# age is calculated and returned each time it is accessed
@property
def age(self):
today = date.today()
return today.year - self._dob.year - ((today.month, today.day) < (self._dob.month, self._dob.day))
# dictionary is customized, removing password for security purposes
@property
def dictionary(self):
dict = {
"name" : self.name,
"uid" : self.uid,
"dob" : self.dob,
"age" : self.age,
"classOf" : self.classOf,
"stockNames" : self.stockNames
}
return dict
# update password, this is conventional setter
def set_password(self, password):
"""Create a hashed password."""
self._password = generate_password_hash(password, method='sha256')
# check password parameter versus stored/encrypted password
def is_password(self, password):
"""Check against hashed password."""
result = check_password_hash(self._password, password)
return result
# output content using json dumps, this is ready for API response
def __str__(self):
return json.dumps(self.dictionary)
# output command to recreate the object, uses attribute directly
def __repr__(self):
return f'User(name={self._name}, uid={self._uid}, password={self._password},dob={self._dob},classOf={self.classOf},stockNames={self.stockNames},)'
def tester(users, uid, psw):
result = None
for user in users:
# test for match in database
if user.uid == uid and user.is_password(psw): # check for match
print("* ", end="")
result = user
# print using __str__ method
print(str(user))
return result
if __name__ == "__main__":
u1 = User(name='Thomas Edison', uid='toby', password='123toby', dob=date(1847, 2, 11), classOf=1865, stockNames=["APPL", "MSFT", "NVDA", "AMZN"],)
u2 = User(name='Quinn Bireley', uid='quack', password='quinnbireley3.14', dob=date(2006, 7, 22), classOf=2025, stockNames=["APPL", "MSFT", "NVDA", "AMZN"],)
users = [u1, u2]
tester(users, u2.uid, "quinnbireley3.14")
print("JSON ready string:\n", u1, "\n")
print("Raw Variables of object:\n", vars(u1), "\n")
print("Raw Attributes and Methods of object:\n", dir(u1), "\n")
print("Representation to Re-Create the object:\n", repr(u1), "\n")
{"name": "Thomas Edison", "uid": "toby", "dob": "02-11-1847", "age": 175, "classOf": 1865, "stockNames": ["APPL", "MSFT", "NVDA", "AMZN"]} {"name": "Quinn Bireley", "uid": "quack", "dob": "10-05-2007", "age": 16, "classOf": 2025, "stockNames": ["APPL", "MSFT", "NVDA", "AMZN"]} JSON ready string: {"name": "Thomas Edison", "uid": "toby", "dob": "02-11-1847", "age": 175, "classOf": 1865, "stockNames": ["APPL", "MSFT", "NVDA", "AMZN"]}
Raw Variables of object: {'_name': 'Thomas Edison', '_uid': 'toby', '_password': 'sha256$oaU1ChCSPEVVGBVK$9cbb9a0281d4c7625509aa346dfc41543939764f1ccab48ac6bb5c6ef331d623', '_dob': datetime.date(1847, 2, 11), '_classOf': 1865, '_stockNames': ['APPL', 'MSFT', 'NVDA', 'AMZN']}
Raw Attributes and Methods of object: ['class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', '_classOf', '_dob', '_name', '_password', '_stockNames', '_uid', 'age', 'classOf', 'dictionary', 'dob', 'is_password', 'is_uid', 'name', 'set_password', 'stockNames', 'uid']
Representation to Re-Create the object: User(name=Thomas Edison, uid=toby, password=sha256$oaU1ChCSPEVVGBVK$9cbb9a0281d4c7625509aa346dfc41543939764f1ccab48ac6bb5c6ef331d623,dob=1847-02-11,classOf=1865,stockNames=['APPL', 'MSFT', 'NVDA', 'AMZN'],)