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

      This has been used in other notebooks, and allows for a database to creates without having seperate api and python files. Like in our flask for our CPT, we needed to have one file to create an API and the other to create the SQL database.

    2. SQLAlchemy db object

      Allows for communication between python and databased, which is needed for CRUD. We also used this in our CPT when we iterated through databases, and displayed (read) from frontend as well.

"""
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 :defines the object that we will be using CRUD operations on - db.Model inheritance: we are able to organize our objects into a database, and display them in our sqlite.db folder
    • init method: allows class to initialize object's attributes
    • @property: creates and returns object, @<column>.setter: sets column for sqlite.db table
    • create (creates new object(reads existing database), update(changes object in existing database), delete methods(removes object from database)
""" database dependencies to support sqlite examples """
import datetime
from datetime import datetime
import json

from sqlalchemy.exc import IntegrityError
from werkzeug.security import generate_password_hash, check_password_hash

''' Tutorial: https://www.sqlalchemy.org/library.html#tutorials, try to get into 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
class Client(db.Model):
    __tablename__ = 'clients'  # table name is plural, class name is singular

    # Define the Client schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _product = db.Column(db.String(255), unique=False, nullable=False)
    _ingredients = db.Column(db.String(255), unique=False, nullable=False)
    _skinType = db.Column(db.String(255), unique=False, nullable=False)
    _date = db.Column(db.String(255), unique=False, nullable=False)
    _pid = db.Column(db.String(255), unique=False, nullable=False)


    def __init__(self, product, pid, ingredients, date, skinType):
        self._product = product    # variables with self prefix become part of the object, 
        self._ingredients = ingredients
        self._date = date
        self._pid = pid
        self._skinType = skinType


    # a name getter method, extracts name from object
    @property
    def product(self):
        return self._product
    
    # a setter function, allows name to be updated after initial object creation
    @product.setter
    def product(self, product):
        self._product = product

    # a name getter method, extracts name from object
    @property
    def pid(self):
        return self._pid
    
    # a setter function, allows name to be updated after initial object creation
    @pid.setter
    def pid(self, pid):
        self._pid = pid

        # a getter method, extracts email from object
    @property
    def ingredients(self):
        return self._ingredients
    
    # a setter function, allows name to be updated after initial object creation
    @ingredients.setter
    def ingredients(self, ingredients):
        self._ingredients = ingredients

            # a getter method, extracts email from object
    @property
    def skinType(self):
        return self._skinType
    
    # a setter function, allows name to be updated after initial object creation
    @skinType.setter
    def skinType(self, skinType):
        self._skinType = skinType

    # a getter method, extracts email from object
    @property
    def date(self):
        return self._date
    
    # a setter function, allows name to be updated after initial object creation
    @date.setter
    def date(self, date):
        self._date = date
    
    # output content using str(object) in human readable form, uses getter
    # output content using json dumps, this is ready for API response
    def __str__(self):
        return json.dumps(self.read())

    # CRUD create/add a new record to the table
    # returns self or None on error
    def create(self):
        try:
            # creates a person object from Client(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None

    # CRUD read converts self to dictionary
    # returns dictionary
    def read(self):
        return {
            "product": self._product,
            "ingredients": self._ingredients,
            "date": self._date,
            "skinType": self._skinType,
            "pid": self._pid,
        }

    # CRUD update: updates product
    # returns self
    def update(self, product=""):
        """only updates values with length"""
        if len(product) > 0:
            self.product = product
        db.session.commit()
        return self

    # CRUD delete: remove self and product
    # None
    def delete(self, product):
        db.session.delete(self)
        db.session.delete(product)
        db.session.commit()
        return None

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
    2. User Object Constructors
    3. Try / Except
"""Database Creation and Testing """

# Builds working data for testing
def initClients():
    with app.app_context():
        """Create database and tables"""
        db.create_all() # uses the database model to put objects (defined below) into a table
        """Tester data for table""" # uses earlir defined class to create objects
        u1 = Client(product='Cetaphil Gentle Skin Cleanser', pid='001', skinType='dry', ingredients='Water, Glycerin, Cocamidopropyl Betaine, Disodium Laureth Sulfosuccinate, Sodium Cocoamphoacetate, Panthenol, Niacinamide, Pantolactone, Acrylates/C10-30 Alkyl Acrylate Crosspolymer, Sodium Benzoate, Masking Fragrance, Sodium Chloride, Citric Acid', date='2023')
        u2 = Client(product='Alaffia Everyday Coconut Face Toner', pid='002', skinType='dry', ingredients='Azadirachta indica (neem) leaf aqueous extract, Carica Papaya (Papaya) Leaf Aqueous Extract, Cocos Nucifera (Coconut) Water, Glycerin, Lavandula Hybrida (Lavender) Oil, Phenoxyethanol, Potassium Sorbate, Ascorbic Acid, Cocos Nucifera (Coconut) Extract', date='2023')
        u3 = Client(product='Laurel Skin Deep Clarity Oil Cleanser', pid='003', skinType='combination', ingredients='Safflower Seed Oil, Sunflower Seed Oil, Sesame Seed Oil, Tamanu Oil, Black Cumin Seed Oil, Whole Plant Extracts of Rosemary, Calendula, Turmeric, Essential Oils of Green Mandarin, Ylang Ylang, Neroli, Rosemary', date='2023')
        u4 = Client(product='Glow Recipe Avocado Ceramide Redness Relief Serum', pid='004', skinType='combination', ingredients='Water/Aqua/Eau, Glycerin, Jojoba Esters, Persea Gratissima (Avocado) Oil, Persea Gratissima (Avocado) Fruit Extract, Niacinamide, Ceramide NP, Ceramide NS, Ceramide AP, Ceramide EOP, Ceramide EOS, Hydrolyzed Rice Protein, Allantoin, Oryza Sativa (Rice) Bran Oil, Bisabolol, Zingiber Officinale (Ginger) Root Extract, Curcuma Longa (Turmeric) Root Extract, Tocopherol, Palmitoyl Tripeptide-8, Vitis Vinifera (Grape) Fruit Extract, Potassium Palmitoyl Hydrolyzed Wheat Protein, Boswellia Serrata Extract, Populus Tremuloides Bark Extract, Caprylic/Capric Triglyceride, Sodium Phytate, Cetyl Alcohol, Melia Azadirachta Flower Extract, Melia Azadirachta Leaf Extract, Ocimum Sanctum Leaf Extract, Behenic Acid, Cholesterol, Ethylhexylglycerin, Ocimum Basilicum (Basil) Flower/Leaf Extract, Elettaria Cardamomum Seed Extract, Jasminum Officinale (Jasmine) Flower/Leaf Extract, Corallina Officinalis Extract, Sodium Carbonate, Sodium Chloride, Chlorophyllin-Copper Complex (CI 75810), Camellia Sinensis Leaf Extract, Cananga Odorata Flower Extract, Caprooyl Phytosphingosine, Caprooyl Sphingosine, Cucumis Melo (Melon) Fruit Extract, Cucumis Sativus (Cucumber) Fruit Extract, Pyrus Malus (Apple) Fruit Extract, Rose Extract, Rubus Idaeus (Raspberry) Leaf Extract, Ascorbyl Palmitate, Dextran, Hydroxyacetophenone, Xanthan Gum, Butylene Glycol, Carbomer, Cetearyl Olivate, Sorbitan Olivate, 1,2-Hexanediol, Caprylyl Glycol, Hydrogenated Vegetable Oil, Lavandula Angustifolia (Lavender) Flower/Leaf/Stem Extract, Santalum Album (Sandalwood) Wood Extract, Tocopheryl Acetate, Ceteareth-25, Sodium Hydroxide, Phenoxyethanol, Sodium Benzoate', date='2023')
        u5 = Client(product='Paula’s Choice Pore-Reducing Toner', pid='005', skinType='oily', ingredients='Water, Glycerin, Butylene Glycol, Niacinamide, Adenosine Triphosphate, Anthemis Nobilis (Chamomile) Flower Extract (anti-irritant), Arctium Lappa (Burdock) Root Extract, Hydrolyzed Jojoba Esters, Hydrolyzed Vegetable Protein, Sodium PCA, Panthenol, Sodium Hyaluronate, Sodium Chondroitin Sulfate, Ceramide 3, Ceramide 6 II, Ceramide 1, Phytosphingosine, Cholesterol, Tetrahexyldecyl Ascorbate, Oleth-10, DEA-Oleth-10 Phosphate, Sodium Lauroyl Lactylate, Polysorbate-20, Caprylyl Glycol, Hexylene Glycol, Sodium Citrate, Xanthan Gum, Trisodium EDTA, Phenoxyethanol', date='2023')
        u6 = Client(product='Drunk Elephant Beste No. 9 Jelly Cleanser', pid='006', skinType='oily',ingredients='Water/Aqua/Eau, Glycerin, Cocamidopropyl Betaine, Coco-Glucoside, Sodium Lauroyl Methyl Isethionate, Cocamidopropyl Hydroxysultaine, Sodium Methyl Oleoyl Taurate, Propanediol, Aloe Barbadensis Leaf Extract, Glycolipids, Linoleic Acid, Linolenic Acid, Lauryl Glucoside, Cucumis Melo Cantalupensis Fruit Extract, Sclerocarya Birrea Seed Oil, Dipotassium Glycyrrhizate, Tocopherol, Citric Acid, Phenoxyethanol, Sodium Hydroxide, Sodium Benzoate, Sodium Chloride, Polylysine', date='2023')

        clients = [u1, u2, u3, u4, u5, u6]

        """Builds sample user/note(s) data"""
        for client in clients:
            try: # tries to create the new object in the table
                '''add user to table'''
                object = client.create()
                print(f"Created new product {object.product}")
            except:  # error raised if object nit created, doesn't create if duplicate
                '''fails with bad or duplicate data'''
                print(f"Records exist product {client.product}, or error.")
                
initClients()
Created new product Cetaphil Gentle Skin Cleanser
Created new product Alaffia Everyday Coconut Face Toner
Created new product Laurel Skin Deep Clarity Oil Cleanser
Created new product Glow Recipe Avocado Ceramide Redness Relief Serum
Created new product Paula’s Choice Pore-Reducing Toner
Created new product Drunk Elephant Beste No. 9 Jelly Cleanser

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. Client.query.filter_by:filters through existing database to determine whether a product already exists 2. client.pid: uses product id to verify or authenticate before completing functions
# uid to product, password to pid
def find_by_product(product):
    with app.app_context():
        client = Client.query.filter_by(_product=product).first()
    return client # returns user object

# Check credentials by finding user and verify password
def check_credentials(product, pid):
    # query email and return user record
    client = find_by_product(product)
    if client == None:
        return False
    if (client.is_pid(pid)):
        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. client.find_by_product() and try/except:finds object that includes user inputted product, then outputs whether it is found or not (keep going) 2. client = Client(...): initializes object to "client"
    2. client.create() and try/except: allows the create function to add an object to the database
def create():
    # optimize user time to see if uid exists
    product = input("Enter product name:")
    client = find_by_product(product)
    try:
        print("Found\n", client.read())
        return
    except:
        pass # keep going
    
    # request value that ensure creating valid object
    product = input("Enter product name:")
    ingredients = input("Enter product ingredients:")
    skinType = input("Enter skin type best for product:")
    date = input("Enter production year:")
    pid = input("Enter product ID:")
    
    # Initialize User object before date
    client = Client(product=product, 
                ingredients=ingredients, 
                skinType=skinType, date=date, pid=pid
                )
    
    
    # write object to database
    with app.app_context():
        try:
            object = client.create()
            print("Created\n", object.read())
        except:  # error raised if object not created
            print("Unknown error product {product}")

create()
Created
 {'product': 'sample prod 2', 'ingredients': 'sample ingred 2', 'date': 'sample prod year 2', 'skinType': 'sample st 2', 'pid': '009'}

Reading users table in sqlite.db

Uses SQLALchemy query.all method to read data

  • Comment on purpose of following
    1. Client.query.all:reads all objects in the table 2. json_ready assignment, google List Comprehension: changes format from sql to json
# SQLAlchemy extracts all users from database, turns each user into JSON
def read():
    with app.app_context():
        table = Client.query.all()
    json_ready = [client.read() for client in table] # "List Comprehensions", for each user add user.read() to list
    return json_ready

read()
[{'product': 'Cetaphil Gentle Skin Cleanser',
  'ingredients': 'Water, Glycerin, Cocamidopropyl Betaine, Disodium Laureth Sulfosuccinate, Sodium Cocoamphoacetate, Panthenol, Niacinamide, Pantolactone, Acrylates/C10-30 Alkyl Acrylate Crosspolymer, Sodium Benzoate, Masking Fragrance, Sodium Chloride, Citric Acid',
  'date': '2023',
  'skinType': 'dry',
  'pid': '001'},
 {'product': 'Alaffia Everyday Coconut Face Toner',
  'ingredients': 'Azadirachta indica (neem) leaf aqueous extract, Carica Papaya (Papaya) Leaf Aqueous Extract, Cocos Nucifera (Coconut) Water, Glycerin, Lavandula Hybrida (Lavender) Oil, Phenoxyethanol, Potassium Sorbate, Ascorbic Acid, Cocos Nucifera (Coconut) Extract',
  'date': '2023',
  'skinType': 'dry',
  'pid': '002'},
 {'product': 'Laurel Skin Deep Clarity Oil Cleanser',
  'ingredients': 'Safflower Seed Oil, Sunflower Seed Oil, Sesame Seed Oil, Tamanu Oil, Black Cumin Seed Oil, Whole Plant Extracts of Rosemary, Calendula, Turmeric, Essential Oils of Green Mandarin, Ylang Ylang, Neroli, Rosemary',
  'date': '2023',
  'skinType': 'combination',
  'pid': '003'},
 {'product': 'Glow Recipe Avocado Ceramide Redness Relief Serum',
  'ingredients': 'Water/Aqua/Eau, Glycerin, Jojoba Esters, Persea Gratissima (Avocado) Oil, Persea Gratissima (Avocado) Fruit Extract, Niacinamide, Ceramide NP, Ceramide NS, Ceramide AP, Ceramide EOP, Ceramide EOS, Hydrolyzed Rice Protein, Allantoin, Oryza Sativa (Rice) Bran Oil, Bisabolol, Zingiber Officinale (Ginger) Root Extract, Curcuma Longa (Turmeric) Root Extract, Tocopherol, Palmitoyl Tripeptide-8, Vitis Vinifera (Grape) Fruit Extract, Potassium Palmitoyl Hydrolyzed Wheat Protein, Boswellia Serrata Extract, Populus Tremuloides Bark Extract, Caprylic/Capric Triglyceride, Sodium Phytate, Cetyl Alcohol, Melia Azadirachta Flower Extract, Melia Azadirachta Leaf Extract, Ocimum Sanctum Leaf Extract, Behenic Acid, Cholesterol, Ethylhexylglycerin, Ocimum Basilicum (Basil) Flower/Leaf Extract, Elettaria Cardamomum Seed Extract, Jasminum Officinale (Jasmine) Flower/Leaf Extract, Corallina Officinalis Extract, Sodium Carbonate, Sodium Chloride, Chlorophyllin-Copper Complex (CI 75810), Camellia Sinensis Leaf Extract, Cananga Odorata Flower Extract, Caprooyl Phytosphingosine, Caprooyl Sphingosine, Cucumis Melo (Melon) Fruit Extract, Cucumis Sativus (Cucumber) Fruit Extract, Pyrus Malus (Apple) Fruit Extract, Rose Extract, Rubus Idaeus (Raspberry) Leaf Extract, Ascorbyl Palmitate, Dextran, Hydroxyacetophenone, Xanthan Gum, Butylene Glycol, Carbomer, Cetearyl Olivate, Sorbitan Olivate, 1,2-Hexanediol, Caprylyl Glycol, Hydrogenated Vegetable Oil, Lavandula Angustifolia (Lavender) Flower/Leaf/Stem Extract, Santalum Album (Sandalwood) Wood Extract, Tocopheryl Acetate, Ceteareth-25, Sodium Hydroxide, Phenoxyethanol, Sodium Benzoate',
  'date': '2023',
  'skinType': 'combination',
  'pid': '004'},
 {'product': 'Paula’s Choice Pore-Reducing Toner',
  'ingredients': 'Water, Glycerin, Butylene Glycol, Niacinamide, Adenosine Triphosphate, Anthemis Nobilis (Chamomile) Flower Extract (anti-irritant), Arctium Lappa (Burdock) Root Extract, Hydrolyzed Jojoba Esters, Hydrolyzed Vegetable Protein, Sodium PCA, Panthenol, Sodium Hyaluronate, Sodium Chondroitin Sulfate, Ceramide 3, Ceramide 6 II, Ceramide 1, Phytosphingosine, Cholesterol, Tetrahexyldecyl Ascorbate, Oleth-10, DEA-Oleth-10 Phosphate, Sodium Lauroyl Lactylate, Polysorbate-20, Caprylyl Glycol, Hexylene Glycol, Sodium Citrate, Xanthan Gum, Trisodium EDTA, Phenoxyethanol',
  'date': '2023',
  'skinType': 'oily',
  'pid': '005'},
 {'product': 'Drunk Elephant Beste No. 9 Jelly Cleanser',
  'ingredients': 'Water/Aqua/Eau, Glycerin, Cocamidopropyl Betaine, Coco-Glucoside, Sodium Lauroyl Methyl Isethionate, Cocamidopropyl Hydroxysultaine, Sodium Methyl Oleoyl Taurate, Propanediol, Aloe Barbadensis Leaf Extract, Glycolipids, Linoleic Acid, Linolenic Acid, Lauryl Glucoside, Cucumis Melo Cantalupensis Fruit Extract, Sclerocarya Birrea Seed Oil, Dipotassium Glycyrrhizate, Tocopherol, Citric Acid, Phenoxyethanol, Sodium Hydroxide, Sodium Benzoate, Sodium Chloride, Polylysine',
  'date': '2023',
  'skinType': 'oily',
  'pid': '006'},
 {'product': 'Lala Retro Whipped Refillable Moisturizer with Ceramides',
  'ingredients': 'Water/Aqua/Eau, Glycerin, Caprylic/ Capric Triglyceride, Isopropyl Isostearate, Pseudozyma Epicola/Camellia Sinensis Seed Oil/Glucose/Glycine Soja (Soybean) Meal/Malt Extract/Yeast Extract Ferment Filtrate, Glyceryl Stearate SE, Cetearyl Alcohol, Palmitic Acid, Stearic Acid, Pentylene Glycol, Plantago Lanceolata Leaf Extract, Adansonia Digitata Seed Oil, Citrullus Lanatus (Watermelon) Seed Oil, Passiflora Edulis Seed Oil, Schinziophyton Rautanenii Kernel Oil, Sclerocarya Birrea Seed Oil, Polyglyceryl-6 Ximenia Americana Seedate, Cholesterol, Ceramide AP, Ceramide EOP,Sodium Hyaluronate Crosspolymer, Ceramide NP, Phytosphingosine, Ceteareth-20, Trisodium Ethylenediamine Disuccinate, Tocopherol, Sodium Lauroyl Lactylate, Sodium Hydroxide, Citric Acid, Carbomer, Xanthan Gum, Caprylyl Glycol, Chlorphenesin, Phenoxyethanol, Ethylhexylglycerin',
  'date': '2023',
  'skinType': 'dry',
  'pid': '007'},
 {'product': 'sample prod 2',
  'ingredients': 'sample ingred 2',
  'date': 'sample prod year 2',
  'skinType': 'sample st 2',
  'pid': '009'}]

Update

import sqlite3

database = 'instance/sqlite.db'

def update():
    product = input("Enter product name to update:")
    pid = input("Enter pid to update:")
    if len(pid) < 2:
        message = "hacked"
        pid = '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 clients SET _pid = ? WHERE _product = ?", (pid, product))
        if cursor.rowcount == 0:
            # The uid was not found in the table
            print(f"No product {product} was not found in the table")
        else:
            print(f"The row with product name {product} the pid 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()
The row with product name sample prod 2 the pid has been successfully updated

Delete

import sqlite3

def delete():
    product = input("Enter product 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 clients WHERE _product = ?", (product,))
        if cursor.rowcount == 0:
            # The uid was not found in the table
            print(f"No product {product} was not found in the table")
        else:
            # The uid was found in the table and the row was deleted
            print(f"The row with product {product} 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()
The row with product sample prod 2 was successfully deleted

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.