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.
  1. Flask app object

Occurs on the line 'app = Flask(name)', its purpose is to set up an Flask object

  1. SQLAlchemy db object

Takes place at the line 'db = SQLAlchemy()' and it serves 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 a new template for the object

  • db.Model inheritance

SQLite table and database is created

  • init method

Helps create the object

  • @property, @<column>.setter

@property creates the values and @< column >.setter changes those values

  • create, read, update, delete methods

The methods used to alter and change an database, interacting with it.

""" 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 college data
    def __init__(self, name, college, gpa, sat, act, gradDate=datetime.today()): # variables that record the users information
        self._name = name
        self._college = college
        self._gpa = gpa
        self._sat = sat
        self._act = act
        if isinstance(gradDate, str):    
            gradDate = date=datetime.today()
        self._gradDate = gradDate

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    @property
    def college(self):
        return self._college

    @college.setter
    def college(self, college):
        self._college = college

    @property
    def gpa(self):
        return self._gpa

    @gpa.setter
    def gpa(self, gpa):
        self._gpa = gpa

    @property
    def sat(self):
        return self._sat

    @sat.setter
    def sat(self, sat):
        self._sat = sat

    @property
    def act(self):
        return self._act

    @act.setter
    def act(self, act):
        self._act = act

    @property
    def gradDate(self):
        gradDate_string = self._gradDate.strftime('%m-%d-%Y')
        return gradDate_string
    
    @gradDate.setter
    def gradDate(self, gradDate):
        if isinstance(gradDate, str):  # not a date type     
            gradDate = date=datetime.today()
        self._gradDate = gradDate


    def __str__(self):
        return json.dumps(self.make_dict())

    # CRUD operations: create, read, update, delete
    # CREATE: Creates new profile
    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 name and college
    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 profile
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None

    # READ: converts self to 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?
  1. Create All Tables from db Object

Works by running the code db.create_all()

  1. User Object Constructors

Is created from the User() and the right formatting

  1. Try / Except

"Try" attempts to create each object and "Except" removes the object and gives 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('Max', 'Purdue', '4.2', '1600','NA',datetime(2023,2,23))
        p2 = Student('Ellen', 'Stanford', '4.9', '1600','36', datetime(2023,1,19))
        p3 = Student('Joe', 'Palomar', '1.9', '1000', '24', datetime(2024,3,29))
        p4 = Student('Gavin', 'MIT', '4.5','1600','33',datetime(2023,8,13))
        p5 = Student('Joey', 'UCDavis', '2.0','1320','25',datetime(2024,6,13))

        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
    1. User.query.filter_by

Filters through the data by the requested parameter in this case uid

  1. user.password

Checks for the password of each of the users

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
  1. user.find_by_uid() and try/except

Try to see if there is a uid that matches the requested uid, and if its not found, it ignores that and keeps going on with the code

  1. user = User(...)

Creates an object for the user

  1. user.dob and try/except

user.dob is the birthday that the user inputs and try sets it into the format and except will set it to today and return an error msg

  1. user.create() and try/except

This creates the user object and try creates the user and except gives and error msg if no object is created

# Inputs, Try/Except, and SQLAlchemy work together to build a valid database object
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()
Created
 {'id': 6, 'name': 'Jishnu', 'college': 'Harvard', 'GPA': '3.9', 'SAT': '1600', 'ACT': '30', 'gradDate': '04-20-2023'}

Reading users table in sqlite.db

Uses SQLALchemy query.all method to read data

  • Comment on purpose of following
  1. User.query.all

Creates and table of all the data in the database

  1. json_ready assignment, google List Comprehension

Its gives a much shorter syntax for the list and makes it easier to read and understand a List

# 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()
[{'id': 1,
  'name': 'Max',
  'college': 'Purdue',
  'GPA': '4.2',
  'SAT': '1600',
  'ACT': 'NA',
  'gradDate': '02-23-2023'},
 {'id': 2,
  'name': 'Ellen',
  'college': 'Stanford',
  'GPA': '4.9',
  'SAT': '1600',
  'ACT': '36',
  'gradDate': '01-19-2023'},
 {'id': 3,
  'name': 'Joe',
  'college': 'Palomar',
  'GPA': '1.9',
  'SAT': '1000',
  'ACT': '24',
  'gradDate': '03-29-2024'},
 {'id': 4,
  'name': 'Gavin',
  'college': 'MIT',
  'GPA': '4.5',
  'SAT': '1600',
  'ACT': '33',
  'gradDate': '08-13-2023'},
 {'id': 5,
  'name': 'Joey',
  'college': 'UCDavis',
  'GPA': '2.0',
  'SAT': '1320',
  'ACT': '25',
  'gradDate': '06-13-2024'},
 {'id': 6,
  'name': 'Jishnu',
  'college': 'Harvard',
  'GPA': '3.9',
  'SAT': '1600',
  'ACT': '30',
  'gradDate': '04-20-2023'}]

Hacks

  • Add this Blog to you own Blogging site. In the Blog add notes and observations on each code cell.
  • Change blog to your own database.
  • Add additional CRUD
    • Add Update functionality to this blog.
    • Add Delete functionality to this blog.

Hacks: Update Functionality

def update():

    nameUp = input("Enter your name:")
    if nameUp is None:
                return {'message': f'Invalid name'}, 210
    
    collegeUp = input("Enter your college:")
    if collegeUp is None:
                return {'message': f'Invalid college'}, 210
    
    gpaUp = input("Enter your GPA:")
    if gpaUp is None:
                return {'message': f'Invalid GPA'}, 210
    
    satUp = input("Enter your SAT score:")
    if satUp is None:
                return {'message': f'Invalid SAT score'}, 210

    actUp = input("Enter your ACT score:")
    if actUp is None:
                return {'message': f'Invalid ACT score'}, 210
    
    with app.app_context():
        # Gets the user through the username
        updateProf = Student.query.filter_by(_name = nameUp).first()
        if updateProf:
            # Updates the score for the user
            updateProf.update(college = collegeUp)
            updateProf.update(gpa = gpaUp)
            updateProf.update(act = actUp)
            updateProf.update(sat = satUp)
            # Returns a dictionary to confirm that the score was updated
            print("Updated:", updateProf.make_dict())
        else:
            # Error message if update fails
            print(f"{nameUp} not found")

update()
 not found

Hacks: Delete Functionality

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()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb Cell 19 in <cell line: 16>()
     <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=11'>12</a>         else:
     <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=12'>13</a>             # Error message if delete fails
     <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=13'>14</a>             print(f"Profile #{getID} not found")
---> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=15'>16</a> delete()

/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb Cell 19 in delete()
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> def delete():
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=1'>2</a> 
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=2'>3</a>     # Gets the ID
----> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=3'>4</a>     getID = int(input("Enter the ID of the profile:"))
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=4'>5</a>     with app.app_context():
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=5'>6</a>         # Gets the user through the ID
      <a href='vscode-notebook-cell://wsl%2Bubuntu/home/jishnus/vscode/CSP-tri1/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X24sdnNjb2RlLXJlbW90ZQ%3D%3D?line=6'>7</a>         profileDelete = Student.query.get(getID)

ValueError: invalid literal for int() with base 10: ''