Database Programming is Program with Data

Each Tri 2 Final Project should be an example of a Program with Data.

Prepare to use SQLite in common Imperative Technique

Schema of Users table in Sqlite.db

Uses PRAGMA statement to read schema.

Describe Schema, here is resource Resource- What is a database schema?

- How data is organized in a database.
  • What is the purpose of identity Column in SQL database?
    • Generating key values for primary key columns.
  • What is the purpose of a primary key in SQL database?
    • Uniquely identifies each record in a table
  • What are the Data Types in SQL table?
    • String, numeric, date and time
import sqlite3

database = 'instance/sqlite.db' # this is location of database

def schema():
    
    # Connect to the database file
    conn = sqlite3.connect(database)

    # Create a cursor object to execute SQL queries
    cursor = conn.cursor()
    
    # Fetch results of Schema
    results = cursor.execute("PRAGMA table_info('users')").fetchall()

    # Print the results
    for row in results:
        print(row)

    # Close the database connection
    conn.close()
    
schema()
(0, 'id', 'INTEGER', 1, None, 1)
(1, '_name', 'VARCHAR(255)', 1, None, 0)
(2, '_uid', 'VARCHAR(255)', 1, None, 0)
(3, '_password', 'VARCHAR(255)', 1, None, 0)
(4, '_dob', 'DATE', 0, None, 0)

Reading Users table in Sqlite.db

Uses SQL SELECT statement to read data

  • What is a connection object? After you google it, what do you think it does?
    • Creates a session with the data source
  • Same for cursor object?
    • Retrieves data one row at a time
  • Look at conn object and cursor object in VSCode debugger. What attributes are in the object?
  • Is "results" an object? How do you know?
    • Yes, it has data and variables
import sqlite3

def read():
    # Connect to the database file
    conn = sqlite3.connect(database)

    # Create a cursor object to execute SQL queries
    cursor = conn.cursor()
    
    # Execute a SELECT statement to retrieve data from a table
    results = cursor.execute('SELECT * FROM users').fetchall()

    # Print the results
    if len(results) == 0:
        print("Table is empty")
    else:
        for row in results:
            print(row)

    # Close the cursor and connection objects
    cursor.close()
    conn.close()
    
read()
(1, 'Thomas Edison', 'toby', 'sha256$DFRnkm3Or5uHl7VU$499f6fe1101659ae17936e62ed8b832b38bc6d1d4ba444404c20238bde3739c5', '1847-02-11')
(2, 'Nikola Tesla', 'niko', 'sha256$kgRYT0rKWoEs825U$7b0d0adf34adea1f5832d01166cae204b77bea9af8b8b21d027cff5866b2b9d5', '2023-03-15')
(3, 'Alexander Graham Bell', 'lex', 'sha256$6x6IuKgohiUA1Ovm$54fad49de978ddac51b4e972e80160ecd09658f9cae75008cb16206090f31b60', '2023-03-15')
(4, 'Eli Whitney', 'whit', 'sha256$tSWjCRTMFWpiD1P7$a8e11bb06844e3fac968f02a65d8111522982670dbf57ab8aa7f61e8957fd14b', '2023-03-15')
(5, 'Indiana Jones', 'indi', 'sha256$QZnQiJVbvaYuNJUp$9d64606ca134832b02001557e8c08de9f3b81911725c4acde454674da1ac7f72', '1920-10-21')
(6, 'Marion Ravenwood', 'raven', 'sha256$tvojNpWzWWW4zmMx$b7db6610e92b1441addad5aec407c0a0939e1c7dac4158f2592ee082a94ee304', '1921-10-21')

Create a new User in table in Sqlite.db

Uses SQL INSERT to add row

  • Compore create() in both SQL lessons. What is better or worse in the two implementations?
    • The imperative implementation makes a little bit more sense to me as it just inserts a new row in the table. The other one creates a new object that is a user in a group of users
  • Explain purpose of SQL INSERT. Is this the same as User init?
    • Init builds the class into a database, sort of, while insert inserts a new row into the database
import sqlite3

def create():
    name = input("Enter your name:")
    uid = input("Enter your user id:")
    password = input("Enter your password")
    dob = input("Enter your date of birth 'YYYY-MM-DD'")
    
    # Connect to the database file
    conn = sqlite3.connect(database)

    # Create a cursor object to execute SQL commands
    cursor = conn.cursor()

    try:
        # Execute an SQL command to insert data into a table
        cursor.execute("INSERT INTO users (_name, _uid, _password, _dob) VALUES (?, ?, ?, ?)", (name, uid, password, dob))
        
        # Commit the changes to the database
        conn.commit()
        print(f"A new user record {uid} has been created")
                
    except sqlite3.Error as error:
        print("Error while executing the INSERT:", error)


    # Close the cursor and connection objects
    cursor.close()
    conn.close()
    
create()
A new user record trentcardall has been created

Updating a User in table in Sqlite.db

Uses SQL UPDATE to modify password

  • What does the hacked part do?
    • If the password someone enters a new password less than 2 characters wrong, the password would change to "gothackednewpassword123"
  • Explain try/except, when would except occur?
    • Except would occur when there is an error with sqlite
  • What code seems to be repeated in each of these examples to point, why is it repeated?
import sqlite3

def update():
    uid = input("Enter user id to update")
    password = input("Enter updated password")
    if len(password) < 2:
        message = "hacked"
        password = 'gothackednewpassword123'
    else:
        message = "successfully updated"

    # Connect to the database file
    conn = sqlite3.connect(database)

    # Create a cursor object to execute SQL commands
    cursor = conn.cursor()

    try:
        # Execute an SQL command to update data in a table
        cursor.execute("UPDATE users SET _password = ? WHERE _uid = ?", (password, uid))
        if cursor.rowcount == 0:
            # The uid was not found in the table
            print(f"No uid {uid} was not found in the table")
        else:
            print(f"The row with user id {uid} the password has been {message}")
            conn.commit()
    except sqlite3.Error as error:
        print("Error while executing the UPDATE:", error)
        
    
    # Close the cursor and connection objects
    cursor.close()
    conn.close()
    
#update()

Delete a User in table in Sqlite.db

Uses a delete function to remove a user based on a user input of the id.

  • Is DELETE a dangerous operation? Why?
    • Yes, it can be, because it removes information from a database that may not be recovered if lost.
  • In the print statemements, what is the "f" and what does {uid} do?
    • f allows an object to be placed directly in the print statement with {} around it. {uid} means to print whatever the uid is right there.
import sqlite3

def delete():
    uid = input("Enter user id to delete")

    # Connect to the database file
    conn = sqlite3.connect(database)

    # Create a cursor object to execute SQL commands
    cursor = conn.cursor()
    
    try:
        cursor.execute("DELETE FROM users WHERE _uid = ?", (uid,))
        if cursor.rowcount == 0:
            # The uid was not found in the table
            print(f"No uid {uid} was not found in the table")
        else:
            # The uid was found in the table and the row was deleted
            print(f"The row with uid {uid} was successfully deleted")
        conn.commit()
    except sqlite3.Error as error:
        print("Error while executing the DELETE:", error)
        
    # Close the cursor and connection objects
    cursor.close()
    conn.close()
    
#delete()

Menu Interface to CRUD operations

CRUD and Schema interactions from one location by running menu. Observe input at the top of VSCode, observe output underneath code cell.

  • Why does the menu repeat?
  • Could you refactor this menu? Make it work with a List?
def menu():
    operation = input("Enter: (C)reate (R)ead (U)pdate or (D)elete or (S)chema")
    if operation.lower() == 'c':
        create()
    elif operation.lower() == 'r':
        read()
    elif operation.lower() == 'u':
        update()
    elif operation.lower() == 'd':
        delete()
    elif operation.lower() == 's':
        schema()
    elif len(operation)==0: # Escape Key
        return
    else:
        print("Please enter c, r, u, or d") 
    menu() # recursion, repeat menu
        
try:
    menu() # start menu
except:
    print("Perform Jupyter 'Run All' prior to starting menu")
(1, 'Thomas Edison', 'toby', 'sha256$DFRnkm3Or5uHl7VU$499f6fe1101659ae17936e62ed8b832b38bc6d1d4ba444404c20238bde3739c5', '1847-02-11')
(2, 'Nikola Tesla', 'niko', 'sha256$kgRYT0rKWoEs825U$7b0d0adf34adea1f5832d01166cae204b77bea9af8b8b21d027cff5866b2b9d5', '2023-03-15')
(3, 'Alexander Graham Bell', 'lex', 'sha256$6x6IuKgohiUA1Ovm$54fad49de978ddac51b4e972e80160ecd09658f9cae75008cb16206090f31b60', '2023-03-15')
(4, 'Eli Whitney', 'whit', 'sha256$tSWjCRTMFWpiD1P7$a8e11bb06844e3fac968f02a65d8111522982670dbf57ab8aa7f61e8957fd14b', '2023-03-15')
(5, 'Indiana Jones', 'indi', 'sha256$QZnQiJVbvaYuNJUp$9d64606ca134832b02001557e8c08de9f3b81911725c4acde454674da1ac7f72', '1920-10-21')
(6, 'Marion Ravenwood', 'raven', 'sha256$tvojNpWzWWW4zmMx$b7db6610e92b1441addad5aec407c0a0939e1c7dac4158f2592ee082a94ee304', '1921-10-21')
(7, 'Trent Cardall', 'trentcardall', 'cardall', '2005-03-09')

Hacks

  • Add this Blog to you own Blogging site. In the Blog add notes and observations on each code cell.
  • In this implementation, do you see procedural abstraction?
  • In 2.4a or 2.4b lecture
    • Do you see data abstraction? Complement this with Debugging example.
    • Use Imperative or OOP style to Create a new Table or do something that applies to your CPT project.

Reference... sqlite documentation

Creating my own table

Using the imperative style, I find it much easier and it makes a lot more sense

import sqlite3

# establish connection and such
conn = sqlite3.connect('instance/sqlite.db')
cursor = conn.cursor()

# creating the table
cars = '''CREATE TABLE reservations(
    _name TEXT,
    _car TEXT,
    _dates TEXT
)'''
cursor.execute(cars)
print("Table created successfully!")

# commit new table and close connection
conn.commit()
conn.close()
Table created successfully!

Create Entry

import sqlite3

def create_entry():
    name = input("Enter your name:")
    car = input("What car will you be using?")
    dates = input("Which dates will you plan on using the car? (MM/DD - MM/DD)")
    
    conn = sqlite3.connect('instance/sqlite.db')
    cursor = conn.cursor()
    
    try:
        # Execute an SQL command to insert data into a table
        cursor.execute("INSERT INTO reservations (_name, _car, _dates) VALUES (?, ?, ?)", (name, car, dates))
        
        # Commit the changes to the database
        conn.commit()
        print(f"A record for {name} has been created")
                
    except sqlite3.Error as error:
        print("Error while executing the INSERT:", error)

    conn.close()

create_entry()
A record for Jared has been created

Update Entry

import sqlite3

def update_entry():
    
    name = input("Enter your name to update")
    choice = input("What do you want to change? Enter c for car and d for dates.")
    if choice == "c":
        car = input("What car do you want to drive instead?")
        conn = sqlite3.connect('instance/sqlite.db')
        cursor = conn.cursor()

        try:
            cursor.execute("UPDATE reservations SET _car = ? where _name = ?", (car, name))
            if cursor.rowcount == 0:
                print(f"The name {name} was not found")
            else:
                print(f"The car for {name} was updated to {car}")
                conn.commit()
        except sqlite3.Error as error:
            print("Error while updating", error)
    
    if choice == "d":
        dates = input("What dates will you be driving instead?")
        conn = sqlite3.connect('instance/sqlite.db')
        cursor = conn.cursor()

        try:
            cursor.execute("UPDATE reservations SET _dates = ? where _name = ?", (dates, name))
            if cursor.rowcount == 0:
                print(f"The name {name} was not found")
            else:
                print(f"The dates for {name} have been updated to {dates}")
                conn.commit()
        except sqlite3.Error as error:
            print("Error while updating", error)

update_entry()
The car for John Smith was updated to Buick Enclave

Delete Entry

import sqlite3

def delete_entry():
    name = input("Enter your name to delete your entry")
    conn = sqlite3.connect('instance/sqlite.db')
    cursor = conn.cursor()

    try:
        cursor.execute("DELETE FROM reservations WHERE _name = ?", (name,))
        if cursor.rowcount == 0:
            print(f"The name {name} was not found in the table")
        else:
            print(f"The entry for {name} has been removed")
            conn.commit()
    except sqlite3.Error as error:
            print("Error while deleting", error)

delete_entry()
The entry for Jared has been removed

Read Entry

import sqlite3

def read_entry():
    conn = sqlite3.connect('instance/sqlite.db')
    cursor = conn.cursor()
    data = cursor.execute('SELECT * FROM reservations').fetchall()
    for row in data:
        print(row)

read_entry()
('John Smith', 'Buick Enclave', '03/22 - 03/26')
('Trent Cardall', 'Ford Taurus', '04/12 - 04/16')
('Joe', 'MINI Convertible', '05/02 - 05/03')
('Luke', 'Subaru Outback', '04/12 - 04/16')
('Darth Vader', 'Buick XC90', '03/26 - 03/28')
('Jared', 'Ferrari', '03/18 - 03/19')
def db_menu():
    option = input("(C)reate, (R)ead, (U)pdate, or (D)elete?")
    if option == "C":
        create_entry()
    elif option == "R":
        read_entry()
    elif option == "U":
        update_entry()
    elif option == "D":
        delete_entry()
    else:
        print("You did not enter one of the correct options. Please try again.")

db_menu()
('John Smith', 'Buick Enclave', '03/22 - 03/26')
('Trent Cardall', 'Ford Taurus', '04/12 - 04/16')
('Joe', 'MINI Convertible', '05/02 - 05/03')
('Luke', 'Subaru Outback', '04/12 - 04/16')
('Darth Vader', 'Buick XC90', '03/26 - 03/28')

Debugging read function

You can see the records of the database, as well as the conn and cursor objects