Unit 2.4a Using Programs with Data, SQLAlchemy
Using Programs with Data is focused on SQL and database actions. Part A focuses on SQLAlchemy and an OOP programming style,
Database and SQLAlchemy
In this blog we will explore using programs with data, focused on Databases. We will use SQLite Database to learn more about using Programs with Data. Use Debugging through these examples to examine Objects created in Code.
-
College Board talks about ideas like
- Program Usage. "iterative and interactive way when processing information"
- Managing Data. "classifying data are part of the process in using programs", "data files in a Table"
- Insight "insight and knowledge can be obtained from ... digitally represented information"
- Filter systems. 'tools for finding information and recognizing patterns"
- Application. "the preserve has two databases", "an employee wants to count the number of book"
-
PBL, Databases, Iterative/OOP
- Iterative. Refers to a sequence of instructions or code being repeated until a specific end result is achieved
- OOP. A computer programming model that organizes software design around data, or objects, rather than functions and logic
- SQL. Structured Query Language, abbreviated as SQL, is a language used in programming, managing, and structuring data
Imports and Flask Objects
Defines and key object creations
- Comment on where you have observed these working? Provide a defintion of purpose.
- Flask app object
- Flask app object occurs on the line 'app = Flask(name)'. The purpose is to set up a Flask object.
- SQLAlchemy db object
- Occurs on the line 'db = SQLAlchemy()'. The purpose is to create properties for db.
"""
These imports define the key objects
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
"""
These object and definitions are used throughout the Jupyter Notebook.
"""
# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
database = 'sqlite:///sqlite.db' # path and filename of database
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = database
app.config['SECRET_KEY'] = 'SECRET_KEY'
db = SQLAlchemy()
# This belongs in place where it runs once per project
db.init_app(app)
Model Definition
Define columns, initialization, and CRUD methods for users table in sqlite.db
- Comment on these items in the class, purpose and defintion.
- class User
- Creates template for making object
- db.Model inheritance
- Creates SQLite table and database.
- init method
- Constructor of the object and helps create it.
-
@property
,@<column>.setter
- @property establishes the values and
@<column>.setter
alters the values inputted. - create, read, update, delete methods
- Common methods for a database. Adds database abilities to help us interact with the data and perform CRUD.
""" database dependencies to support sqliteDB examples """
from random import randrange
import datetime
from datetime import datetime
import os, base64
import json
# from __init__ import app, db
from sqlalchemy.exc import IntegrityError
''' Tutorial: https://www.sqlalchemy.org/library.html#tutorials, try to get into a Python shell and follow along '''
# Define the User class to manage actions in the 'users' table
# -- Object Relational Mapping (ORM) is the key concept of SQLAlchemy
# -- a.) db.Model is like an inner layer of the onion in ORM
# -- b.) User represents data we want to store, something that is built on db.Model
# -- c.) SQLAlchemy ORM is layer on top of SQLAlchemy Core, then SQLAlchemy engine, SQL
# Create class Score to add a scores table to database
class Student(db.Model):
# Table name
__tablename__ = 'students'
# Define the columns for the table
id = db.Column(db.Integer, primary_key=True)
_name = db.Column(db.String(255), unique=False, nullable=False)
_college = db.Column(db.String(255), unique=False, nullable=False)
_gpa= db.Column(db.String(255), unique=False, nullable=False)
_sat = db.Column(db.String(255), unique=False, nullable=False)
_act = db.Column(db.String(255), unique=False, nullable=False)
_gradDate = db.Column(db.Date)
# Construct the profile for Score
def __init__(self, name, college, gpa, sat, act, gradDate=datetime.today()): # variables that record the username, score, and date of score(dos)
self._name = name
self._college = college
self._gpa = gpa
self._sat = sat
self._act = act
if isinstance(gradDate, str): # not a date type
gradDate = date=datetime.today()
self._gradDate = gradDate
# Getter method
@property
def name(self):
return self._name
# Setter function
@name.setter
def name(self, name):
self._name = name
# Getter method
@property
def college(self):
return self._college
# Setter function
@college.setter
def college(self, college):
self._college = college
# Getter method
@property
def gpa(self):
return self._gpa
# Setter function
@gpa.setter
def gpa(self, gpa):
self._gpa = gpa
# Getter method
@property
def sat(self):
return self._sat
# Setter function
@sat.setter
def sat(self, sat):
self._sat = sat
# Getter method
@property
def act(self):
return self._act
# Setter function
@act.setter
def act(self, act):
self._act = act
# Getter method
@property
def gradDate(self):
gradDate_string = self._gradDate.strftime('%m-%d-%Y')
return gradDate_string
# Setter function
@gradDate.setter
def gradDate(self, gradDate):
if isinstance(gradDate, str): # not a date type
gradDate = date=datetime.today()
self._gradDate = gradDate
# # Convert dos to a string
# @property
# def dos(self):
# dos_string = self._dos.strftime('%m-%d-%Y')
# return dos_string
# # Setter function
# @dos.setter
# def dos(self, dos):
# self._dos = dos
# Output content using json dumps
def __str__(self):
return json.dumps(self.make_dict())
# CRUD operations: create, read, update, delete
# CREATE: returns self and returns None if there is error
def create(self):
try:
# creates a user object from Score(db.Model) class
db.session.add(self) # add persists user object onto table
db.session.commit() # SqlAlchemy "unit of work pattern" requires a manual commit
return self
except IntegrityError:
db.session.remove()
return None
# UPDATE: updates username and score
def update(self, name="", college="", gpa="", sat="", act=""):
"""only updates values with length"""
if len(name) > 0:
self.name = name
if len(college) > 0:
self.college = college
if len(gpa) > 0:
self.gpa = gpa
if len(sat) > 0:
self.sat = sat
if len(act) > 0:
self.act = act
db.session.add(self)
db.session.commit()
return self
# DELETE: removes a user
def delete(self):
db.session.delete(self)
db.session.commit()
return None
# READ: converts self to dictionary
# returns a dictionary
def make_dict(self):
return {
"id": self.id,
"name": self.name,
"college": self.college,
"GPA": self.gpa,
"SAT": self.sat,
"ACT": self.act,
"gradDate": self.gradDate
}
Initial Data
Uses SQLALchemy db.create_all() to initialize rows into sqlite.db
- Comment on how these work?
- Create All Tables from db Object
- Works through db.create_all()
- User Object Constructors
- Works through the Student() with the proper parameters(shown in p1 to p5)
- Try / Except
- Try works by attempting to create each object. If it doesn't work, except takes charge, removes the faulty object, and displays an error message.
"""Database Creation and Testing """
# Builds working data for testing
def initStudents():
with app.app_context():
"""Create database and tables"""
db.create_all()
"""Tester data for table"""
p1 = Student('Eric', 'Yale', '4.2', '1600','NA',datetime(2023,1,11))
p2 = Student('Jeff', 'Harvard', '3.2', '1540','30', datetime(2023,1,22))
p3 = Student('Bob', 'Palomar', '4.9', '1600', '36', datetime(2024,6,9))
p4 = Student('???', 'Stanford', '4.5','1400','29',datetime(2024,6,9))
p5 = Student('Bobby', 'UCSD', '2.0','1320','25',datetime(2025,6,10))
profiles = [p1, p2, p3, p4, p5]
"""Builds sample user/note(s) data"""
for profile in profiles:
try:
profile.create()
except IntegrityError:
'''fails with bad or duplicate data'''
db.session.remove()
print(f"Error in creating object: {profile.name}")
initStudents()
Check for given Credentials in users table in sqlite.db
Use of ORM Query object and custom methods to identify user to credentials uid and password
- Comment on purpose of following
- User.query.filter_by
- Filters through the data to find and match the uid in the function's parameters.
- user.password
- Checks to see if there is a password existing for the user.
def find_by_uid(uid):
with app.app_context():
user = User.query.filter_by(_uid=uid).first()
return user # returns user object
# Check credentials by finding user and verify password
def check_credentials(uid, password):
# query email and return user record
user = find_by_uid(uid)
if user == None:
return False
if (user.is_password(password)):
return True
return False
#check_credentials("indi", "123qwerty")
Create a new User in table in Sqlite.db
Uses SQLALchemy and custom user.create() method to add row.
- Comment on purpose of following
- user.find_by_uid() and try/except
- Finds to see if there is already an existing uid that is the same as the inputted uid. Try stops the code if the same uid already exists. If there is no currently existing inputted uid, except makes the code continue running.
- user = User(...)
- Creates the User object.
- user.dob and try/except
- user.dob is the inputted dob. Try sets the dob in the following format. Except sets it to today and returns and error message if the date isn't inputted correctly.
- user.create() and try/except
- Creates the User object. Try creates the user. Except returns an error message if no object is created.
def create():
# request value that ensure creating valid object
name = input("Enter your name:")
if name is None:
return {'message': f'Invalid name'}, 210
college = input("Enter your college:")
if college is None:
return {'message': f'Invalid college'}, 210
gpa = input("Enter your GPA:")
if gpa is None:
return {'message': f'Invalid GPA'}, 210
sat = input("Enter your SAT score:")
if sat is None:
return {'message': f'Invalid SAT score'}, 210
act = input("Enter your ACT score:")
if act is None:
return {'message': f'Invalid ACT score'}, 210
# Initialize User object before date
students = Student(name=name,
college=college,
gpa=gpa,
sat=sat,
act=act
)
# create user.dob, fail with today as dob
gradDate = input("Enter your date of graduation 'MM-DD-YYYY'")
try:
students.gradDate = datetime.strptime(gradDate, '%m-%d-%Y').date()
except ValueError:
students.gradDate = datetime.today()
print(f"Invalid date {gradDate} require MM-DD-YYYY, date defaulted to {students.gradDate}")
# write object to database
with app.app_context():
try:
object = students.create()
print("Created\n", object.make_dict())
except: # error raised if object not created
print(f"Unknown error name {name}")
create()
# SQLAlchemy extracts all users from database, turns each user into JSON
def read():
with app.app_context():
table = Student.query.all()
json_ready = [user.make_dict() for user in table] # "List Comprehensions", for each user add user.read() to list
return json_ready
read()
def update():
nameD = input("Enter your name:")
if nameD is None:
return {'message': f'Invalid name'}, 210
collegeD = input("Enter your college:")
if collegeD is None:
return {'message': f'Invalid college'}, 210
gpaD = input("Enter your GPA:")
if gpaD is None:
return {'message': f'Invalid GPA'}, 210
satD = input("Enter your SAT score:")
if satD is None:
return {'message': f'Invalid SAT score'}, 210
actD = input("Enter your ACT score:")
if actD is None:
return {'message': f'Invalid ACT score'}, 210
with app.app_context():
# Gets the user through the username
profileUpdate = Student.query.filter_by(_name = nameD).first()
if profileUpdate:
# Updates the score for the user
profileUpdate.update(college = collegeD)
profileUpdate.update(gpa = gpaD)
profileUpdate.update(act = actD)
profileUpdate.update(sat = satD)
# Returns a dictionary to confirm that the score was updated
print("Updated:", profileUpdate.make_dict())
else:
# Error message if update fails
print(f"{nameD} not found")
update()
def delete():
# Gets the ID
getID = int(input("Enter the ID of the profile:"))
with app.app_context():
# Gets the user through the ID
profileDelete = Student.query.get(getID)
if profileDelete:
# Deletes the user according to its ID number
profileDelete.delete()
print(f"Profile #{getID} deleted")
else:
# Error message if delete fails
print(f"Profile #{getID} not found")
delete()