NOTE

As you follow along, make sure to fill in the blanks and complete the coding exercises!

Introduction

When building an application that requires users to create accounts or sign in, handling data related to users is crucial. This data can include things like user profiles, preferences, and activity logs, which can be used to personalize the user experience and improve the application's performance.

For example, by storing a user's name and profile picture, the application can address the user by name and display their picture, creating a more personal experience. Activity logs can also be used to track user behavior and help the application recommend new features or improvements.

By learning how to handle data related to users effectively and responsibly, you'll be equipped with the skills and knowledge needed to build robust and user-friendly applications that meet the needs of your users.

For simplicity purposes, we will be lecturing on how one can store and manipulate user data for future utilization.

Here we go!

Establishing Class/User Data and making a new user

In Python, classes are templates used to create objects, which are instances of those classes. Classes define the data elements (attributes) and methods that describe the behavior of the objects. Let's explore how to define a class and create objects in Python.

Example: Defining a User class

class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email

    def display_info(self):
        print(f"Username: {self.username}, Email: {self.email}")

In this example, we define a User class with a constructor method init that takes username and email as arguments. The display_info method is used to print the user information.

In the context of backend functionality, this class can be used to create, manipulate, and manage user data. For example, when a new user signs up for an account, you could create a new User object with their username and email. This object can then be used to perform various operations, such as validating the user's input, storing the user's data in a database, or processing user-related requests.

Creating a new user:

new_user = User("john_doe", "john@example.com")
new_user.display_info()

Lecture Topics:

Establishing Class/User Data and making a new user

In Python, classes are templates used to create objects, which are instances of those classes. Classes define the data elements (attributes) and methods that describe the behavior of the objects. Let's explore how to define a class and create objects in Python.

Example: Defining a User class

class User: def init(self, username, email): self.username = username self.email = email

def display_info(self):
    print(f"Username: {self.username}, Email: {self.email}")

In this example, we define a User class with a constructor method init that takes username and email as arguments. The display_info method is used to print the user information.

Creating a new user:

python

new_user = User("john_doe", "john@example.com") new_user.display_info()

Here, we create a new User object, new_user, with a specified username and email. We then call the display_info method to display the user's information.

Using property decorators (getter and setter)

Property decorators allow developers to access and modify instance data more concisely. The @property decorator creates a getter method, while the @attribute.setter decorator creates a setter method.

Example:

class Employee:
    def __init__(self, employee_id, name):
        self._employee_id = employee_id
        self._name = name

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

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

In this example, the Employee class has a name attribute, which is accessed and modified through the name property getter and setter methods. The _name attribute uses an underscore prefix, which is a convention to indicate it should not be accessed directly.

In the context of backend functionality, this Employee class can be used to model employees within an application. You can create instances of this class to store and manage employee data, and the getter and setter methods can be used to access and modify employee information in a controlled way.

Usage:

employee = Employee(1001, "John Doe")
print(employee.name)  # Get the name using the getter method

employee.name = "Jane Doe"  # Set the name using the setter method
print(employee.name)

employee = Employee(1001, "John Doe") print(employee.name) # Get the name using the getter method

employee.name = "Jane Doe" # Set the name using the setter method print(employee.name)

In the context of backend functionality, the getter and setter methods provide a clean and controlled way to access and modify the attributes of an object. This can be particularly useful when interacting with databases, APIs, or other parts of a web application that require the management and manipulation of object attributes.

CHECK: Explain the function of getters and setters in your own words.

  • Setter: a method that allows us to set or change the value of an attribute in a class.
  • Getter: a method that allows us to access an attribute in a given class
class Car:
    def __init__(self, make, model, year):
        self._make = make
        self._model = model
        self._year = year

    @property
    def make(self):
        return self._make

    @make.setter
    def make(self, new_make):
        self._make = new_make

    @property
    def model(self):
        return self._model

    @model.setter
    def model(self, new_model):
        self._model = new_model

    @property
    def year(self):
        return self._year

    @year.setter
    def year(self, new_year):
        self._year = new_year

Take notes here on property decorators and the purpose they serve:

Students can then practice creating instances of their Car class and using the getter and setter methods to access and modify the car attributes.

In the context of backend functionality, this Car class can be used to model cars within an application. You can create instances of this class to store and manage car data, and the getter and setter methods can be used to access and modify car information in a controlled way.

Overview

WE COVERED: In conclusion, we have covered essential concepts in object-oriented programming using Python, including:

Defining classes and creating objects
Property decorators (getter and setter)
Class methods and static methods
Inheritance and method overriding
Working with multiple objects and class attributes

These concepts provide a solid foundation for understanding how to model real-world entities using classes and objects in Python. By learning to work with classes, objects, and their methods, students can develop more efficient and modular code.

As students become more comfortable with these concepts, they can explore more advanced topics, such as multiple inheritance, abstract classes, encapsulation, and polymorphism. Additionally, they can apply these principles to practical projects like web development with Flask and SQLite, as discussed earlier.

Overall, mastering object-oriented programming will greatly enhance students' ability to develop complex and maintainable software systems.

Databases and SQlite

SQLite is a software library that provides a relational database management system. Unlike other databases, such as MySQL or PostgreSQL, SQLite is embedded within an application, which means it does not require a separate server process to operate. This makes SQLite a great choice for small-scale applications or for use in situations where you don't want to set up a full database server.

In this lesson, we will be demonstrating how to set up a SQLite database in Flask, which provides an easy-to-use interface for interacting with SQLite databases, and we'll walk through the process of setting up a new database, creating tables, and adding data. We'll also cover some basic SQL commands that you can use to interact with your database, including CREATE TABLE, INSERT, SELECT, UPDATE, and DELETE. By the end of this lesson, you'll have a good understanding of how to work with SQLite databases in Flask and be ready to start building your own applications.

Setting up a SQLite database in Flask

One of the key features of flask is its ability to work seamlessly with databases, including SQLite. A database is a collection of data stored in an organized manner that can be easily accessed, managed, and updated.

SQlite database in Flask

from flask import Flask
import sqlite3

# Create a Flask application
app = Flask(__name__)

# Connect to the SQLite database using SQLite3
conn = sqlite3.connect('example.db')

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

# Create a table in the database using SQL commands
cursor.execute('''CREATE TABLE example_table
                 (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')

# Commit the changes to the database
conn.commit()

# Close the connection
conn.close()

Basic SQL commands (create, read, update, delete)

SQL is really useful because it helps people do a bunch of things with the data stored in databases. For example, they can use it to create new tables to organize data, add new data to a table, update data that's already there, or delete data that's no longer needed.

CRUD is an acronym that stands for the fundamental operations that can be performed on a database, which are Create, Read, Update, and Delete. A widely-used lightweight database management system is SQLite, which can be easily integrated with different programming languages.

  • C: To create a new record in a database, you must first define the table structure that will store the data. This can be accomplished using SQL commands such as CREATE. Once the table is created, data can be added to it using the INSERT INTO command.

  • R: To retrieve data from the database, you can use the READ command. You can specify which fields you want to retrieve and the conditions you want to apply using the WHERE clause. There are also several functions available to aggregate and manipulate data.

  • U: To modify existing data in the database, you can use the UPDATE command. You will need to specify which table and fields you want to update, and the conditions you want to apply using the WHERE clause.

  • D: To remove data from the database, you can use the DELETE command

Take notes here on the basic components of SQL: The basic components of SQL are:

  • Database: A collection of tables that hold data.
  • Table: A collection of data that is organized into rows and columns.
  • Column: A vertical series of cells in a table that holds data of the same type.
  • Row: A horizontal series of cells in a table that holds data for a single record.
  • Query: A request for data or information from a database.

CRUD stands for Create, Read, Update, and Delete, which are the four basic functions that can be performed on data in a database.

  • Create: Used to insert new data into a database. The SQL statement used for creating data is INSERT.
  • Read: Used to retrieve data from a database. The SQL statement used for reading data is SELECT.
  • Update: Used to modify existing data in a database. The SQL statement used for updating data is UPDATE.
  • Delete: Used to remove data from a database. The SQL statement used for deleting data is DELETE.
import sqlite3

def menu():
    operation = input("Enter: (C)reate (R)ead (U)pdate or (D)elete")
    if operation.lower() == 'c':
        create()
    elif operation.lower() == 'r':
        read()
    elif operation.lower() == 'u':
        update()
    elif operation.lower() == 'd':
        delete()
    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")

This block of code is a menu function that helps with create, read, update, and delete (CRUD) tasks and displays the schema. The menu function acts as a control point that directs the program to call different functions based on what the user wants to do. When users enter their preferred action, the input is checked to see which function to use. The menu function is created with no arguments and is called repeatedly, displaying the menu options until the user decides to leave.

Creating a Database

import sqlite3

def create_database():
    # Connect to the database (will create it if it doesn't exist)
    connection = sqlite3.connect('instance/professors.db')
    cursor = connection.cursor()

    # Create the professors table if it doesn't already exist
    cursor.execute('''CREATE TABLE IF NOT EXISTS professors (
                    name TEXT,
                    field TEXT,
                    rating REAL,
                    reviews TEXT
                )''')

    # Commit changes and close the connection
    connection.commit()
    connection.close()

# Call the function to create the database
create_database()

Create Function:

import sqlite3

def create():
   database = 'instance/professors.db'
   name = input("Enter the professor's name: ")
   field = input("Enter the professor's field of expertise: ")
   rating = input("Enter the professor's rating (out of 10): ")
   reviews = input("Enter any reviews or comments about the professor: ")


   # Connect to the database and create a cursor to execute SQL commands
   connection = sqlite3.connect(database)
   cursor = connection.cursor()


   try:
       # Execute SQL to insert record into db
       cursor.execute("INSERT INTO professors (name, field, rating, reviews) VALUES (?, ?, ?, ?)", (name, field, rating, reviews))
       # Commit the changes
       connection.commit()
       print(f"{name} has been added to the list of coding professors.")
              
   except sqlite3.Error as error:
       print("Error while inserting record", error)


   # Close cursor and connection
   cursor.close()
   connection.close()

create()

The create() function allows users to input information about a coding professor and store it in a SQLite database named 'professors.db'. This script prompts the user for the professor's name, field of expertise, rating out of 10, and any reviews or comments about the professor. It then establishes a connection to the SQLite database and creates a cursor object for executing SQL commands.

Read Function

import sqlite3

def read():
    try:
        # Open a connection to the database and create a cursor
        connection = sqlite3.connect('instance/professors.db')
        cursor = connection.cursor()

        # Fetch all records from the professors table
        cursor.execute("SELECT * FROM professors")
        rows = cursor.fetchall()

        # If there are any records, print them
        if len(rows) > 0:
            print("List of coding professors:")
            for row in rows:
                print(f"Name: {row[0]}\nField of expertise: {row[1]}\nRating: {row[2]}\nReviews: {row[3]}\n")
        else:
            print("There are no coding professors in the list.")

    except sqlite3.Error as error:
        print("Error while connecting to the database:", error)

    finally:
        # Close the cursor and the connection to the database
        cursor.close()
        connection.close()

read()

This code demonstrates how to read data from a SQLite database using Python and the sqlite3 library. The first step is to establish a connection to the database and create a cursor object to execute SQL commands. Then, a SELECT query is executed to fetch all records from the "professors" table. If there are any records, the code iterates through each record and prints out the name, field of expertise, rating, and reviews for each coding professor. If there are no records in the table, a message indicating so is printed.

Update Function

import sqlite3

def update():
    database = 'instance/professors.db'
    connection = sqlite3.connect(database)
    cursor = connection.cursor()
    
    try:
        # Get the professor's name to update
        name = input("Enter the name of the professor to update: ")
        
        # Retrieve the current record from the database
        cursor.execute("SELECT * FROM professors WHERE name=?", (name,))
        record = cursor.fetchone()
        
        # If the professor is found, update the record
        if record:
            print("Enter the new information for the professor:")
            field = input(f"Current field: {record[1]}\nNew field: ")
            rating = input(f"Current rating: {record[2]}\nNew rating: ")
            reviews = input(f"Current reviews: {record[3]}\nNew reviews: ")
            
            # Execute SQL to update the record
            cursor.execute("UPDATE professors SET field=?, rating=?, reviews=? WHERE name=?", (field, rating, reviews, name))
            connection.commit()
            
            print(f"{name}'s record has been updated.")
        
        # If the professor is not found, notify the user
        else:
            print(f"No record found for {name}.")
    
    except sqlite3.Error as error:
        print("Error while updating record", error)
    
    # Close cursor and connection
    cursor.close()
    connection.close()
update ()

This is an implementation of an update function for the professors database using the sqlite3 module in Python. The function first establishes a connection to the database file 'instance/professors.db' and creates a cursor object to execute SQL commands. It prompts the user to enter the name of the professor to update and retrieves the corresponding record from the database using a SELECT statement with a WHERE clause to match the professor's name. If the professor is found in the database, the user is prompted to enter new information for the professor's field of expertise, rating, and reviews. The function then executes an UPDATE statement with the new information to update the record in the database.

Delete Function

import sqlite3


def delete():
    # Connect to the database and create a cursor
    connection = sqlite3.connect('instance/professors.db')
    cursor = connection.cursor()

    # Prompt the user for the name of the professor to delete
    name = input("Enter the name of the professor you want to delete: ")

    # Use a SQL query to find the professor with the given name
    cursor.execute("SELECT * FROM professors WHERE name=?", (name,))
    row = cursor.fetchone()

    # If the professor exists, confirm deletion and delete the record
    if row:
        confirm = input(f"Are you sure you want to delete {name}? (y/n): ")
        if confirm.lower() == 'y':
            cursor.execute("DELETE FROM professors WHERE name=?", (name,))
            connection.commit()
            print(f"{name} has been deleted from the list of coding professors.")
    else:
        print(f"{name} not found in the list of coding professors.")

    # Close the cursor and the connection to the database
    cursor.close()
    connection.close()

delete()

This code is a Python function for deleting a record from a SQLite database. The function prompts the user to input the name of the professor they want to delete. It then uses a SQL query to search for the professor in the database. If the professor is found, the user is prompted to confirm the deletion. If the user confirms, the function executes a SQL command to delete the record from the database. The function also prints a message confirming that the professor has been deleted from the list of coding professors. If the professor is not found in the database, the function prints a message indicating that the professor is not in the list.

Our Project ... in the works

SAM Messaging System

Get started with your own!

import sqlite3

# specify the name of the database file
db_file = "<fill in the name of the database file here>.db"

# create a connection to the database
conn = sqlite3.connect(db_file)

# create a cursor object
cursor = conn.cursor()

# add code here to create tables and insert data into the database

# commit the changes and close the connection
conn.commit()
conn.close()

HACKS

  • Make sure to fill in all blanks, take notes when prompted, and at least attempt each of the interactive coding exercises. (0.45) DONE

  • Create your own database and create an algorithm that can insert, update, and delete data related to user. Points will be awarded based on effort and success. (0.45)

    • Extra Credit: Connect your backend to a visible frontend!
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
database = 'sqlite:///instance/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)
class Car(db.Model):
    __tablename__ = 'cars'  # table name is plural, class name is singular

    # Define the User schema with "vars" from object
    id = db.Column(db.Integer, unique=True, primary_key=True)
    _uid = db.Column(db.String(255), unique=True, nullable=False)
    _brand = db.Column(db.String(255), unique=False, nullable=False)
    _color = db.Column(db.Integer, unique=False, nullable=False)
    _powersource = db.Column(db.String(255), unique=False, nullable=False)
    _type = db.Column(db.String(255), unique=False, nullable=False)
   

    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, uid, brand, color, type, powersource):
        self._brand = brand
        self._color = color
        self._powersource = powersource
        self._type = type 
        self._uid = uid
    
    # a getter method, extracts uid from object
    @property
    def uid(self):
        return self._uid
    
    # a setter function, allows uid to be updated after initial object creation
    @uid.setter
    def uid(self, uid):
        self._uid = uid
        
    # gets the brand the car
    @property
    def brand(self):
        return self._brand
    
    # a setter function, allows brand to be updated after initial object creation
    @brand.setter
    def brand(self, brand):
        self._brand = brand

     # gets the color of the car
    @property
    def color(self):
        return self._color
    
    # a setter function, allows color to be updated after initial object creation
    @color.setter
    def color(self, color):
        self._color = color

    # gets the type of the manufacturer or the car
    @property
    def type(self):
        return self._type
    
    # a setter function, allows type to be updated after initial object creation
    @type.setter
    def type(self, type):
        self._type = type
    
    # a powersource getter
    @property
    def powersource(self):
        return self._powersource

    # a setter function to set the car's powersource
    @powersource.setter
    def powersource(self, powersource):
        self._powersource = powersource
    
            
    # 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 User(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Users 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 {
            "id": self.id,
            "brand" : self.brand,
            "color" : self.color,
            "powersource" : self.powersource,
            "type" : self.type,
            "uid": self.uid
        }
     
    def update(self, brand="", color="", powersource="", type="", uid=""):
        """only updates values with length"""
        if len(brand) > 0:
            self.brand = brand
        if len(color) > 0:
            self.color = color
        if len(powersource) > 0:
            self.powersource = powersource
        if len(type) > 0:
            self.type = type
        if len(uid) > 0:
            self.uid = uid
        db.session.add(self) # performs update when id exists\n",
        db.session.commit()
        return self

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None
def initCars():
    with app.app_context():
        """Create database and tables"""
        db.create_all()

        """Tester data for table"""
        c1 = Car(uid='1', brand='Acura', color='gray', powersource='ice', type='suv')
        c2 = Car(uid='2', brand='Hyundai', color='red', powersource='ice', type='sedan') 
        c3 = Car(uid='3', brand='Mazda', color='white', powersource='ice', type='sedan')
        c4 = Car(uid='4', brand='Honda', color='gray', powersource='ice', type='suv')
        c5 = Car(uid='5', brand='Dodge', color='black', powersource='ice', type='suv')
        c6 = Car(uid='6', brand='Toyota', color='white', powersource='ice', type='truck') 
        c7 = Car(uid='7', brand='Hyundai', color='blue', powersource='ice', type='sedan')
        c8 = Car(uid='8', brand='Chevrolet', color='gray', powersource='ice', type='truck')
        c9 = Car(uid='9', brand='Jeep', color='gray', powersource='ice', type='suv')
        c10 = Car(uid='10', brand='Nissan', color='silver', powersource='ice', type='sedan') 
        c11 = Car(uid='11', brand='Lexus', color='black', powersource='ice', type='sedan')
        c12 = Car(uid='12', brand='Kia', color='red', powersource='ice', type='suv')
        c13 = Car(uid='13', brand='Mazda', color='red', powersource='ice', type='truck')
        c14 = Car(uid='14', brand='Ford', color='white', powersource='ice', type='sedan') 
        c15 = Car(uid='15', brand='Kia', color='red', powersource='ice', type='truck')
        c16 = Car(uid='16', brand='Ford', color='gray', powersource='ice', type='suv')
        c17 = Car(uid='17', brand='Jeep', color='red', powersource='ice', type='truck')
        c18 = Car(uid='18', brand='Toyota', color='red', powersource='electric', type='suv') 
        c19 = Car(uid='19', brand='Kia', color='silver', powersource='ice', type='truck')
        c20 = Car(uid='20', brand='Honda', color='white', powersource='ice', type='suv')
        c21 = Car(uid='21', brand='Hyundai', color='white', powersource='ice', type='sedan')
        c22 = Car(uid='22', brand='Chevrolet', color='white', powersource='ice', type='suv') 
        c23 = Car(uid='23', brand='Jeep', color='white', powersource='ice', type='suv')
        c24 = Car(uid='24', brand='BMW', color='gray', powersource='ice', type='sedan')
        c25 = Car(uid='25', brand='Ferrari', color='yellow', powersource='ice', type='sports')
        c26 = Car(uid='26', brand='Tesla', color='red', powersource='electric', type='suv') 
        c27 = Car(uid='27', brand='Tesla', color='blue', powersource='electric', type='suv')
        c28 = Car(uid='28', brand='Ford', color='white', powersource='electric', type='truck')
        c29 = Car(uid='29', brand='Ford', color='blue', powersource='electric', type='truck')
        c30 = Car(uid='30', brand='Audi', color='black', powersource='electric', type='suv') 
        c31 = Car(uid='31', brand='Ferrari', color='red', powersource='electric', type='sports')
        c32 = Car(uid='32', brand='Mercedes', color='silver', powersource='electric', type='sedan')
        c33 = Car(uid='33', brand='Mazda', color='silver', powersource='electric', type='suv')
        c34 = Car(uid='34', brand='Nissan', color='blue', powersource='electric', type='suv') 
        c35 = Car(uid='35', brand='Subaru', color='red', powersource='electric', type='suv')

        cars = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, c23, c24, c25, c26, c27, c28, c29, c30, c31, c32, c33, c34, c35]

        """Builds sample car/note(s) data"""
        for car in cars:
            try:
                object = car.create()
                print(f"Created new uid {object.uid}")
                
            except:  # error raised if object nit created
                '''fails with bad or duplicate data'''
                print(f"Records exist uid {car.uid}, or error.")
initCars()
def find_by_caruid(uid):
    with app.app_context():
        car = Car.query.filter_by(_uid=uid).first()
    return car # returns user object

def createCar():
    uid = input("Enter your car id:")
    car = find_by_caruid(uid)
    try:
        print("Found\n", car.read())
        return
    except:
        pass # keep going

    # request value that ensure creating valid object
    brand = input("What brand is the car?")
    color = input("What color is the car?")
    powersource = input("What is the car's powersource?")
    type = input("What type of car is it?") 
    
    # Initialize User object before date
    car = Car(brand=brand,
                color=color, 
                powersource=powersource,
                type=type,
                uid=uid
                )
       
    # write object to database
    with app.app_context():
        try:
            object = car.create()
            print("Created\n", object.read())
        except:  # error raised if object not created
            print("Unknown error powersource {uid}")
        
createCar()
def readCar():
    with app.app_context():
        table = Car.query.all()
    json_ready = [car.read() for car in table] # "List Comprehensions", for each car add car.read() to list
    return json_ready

readCar()
def updateCar(uid, new_brand, new_color, new_powersource, new_type):
    with app.app_context():
        car = db.session.query(Car).filter_by(_uid=uid).first()
        try:
            print("Found\n", car.read())

            car.update(new_brand, new_color, new_powersource, new_type)
        
            return
        except:
            pass # keep going

uid = input("Enter your car id:")

new_brand = input("Enter new brand:")
new_color = input("Enter new color:")
new_powersource = input("Enter new powersource:")
new_type = input("Enter new type:")

updateCar(uid, new_brand, new_color, new_powersource, new_type)
def deleteCar(uid):
    with app.app_context():
        car = db.session.query(Car).filter_by(_uid=uid).first()
        try:
            print("Found\n", car.read())

            car.delete()
        
            return
        except:
            pass # keep going

uid = input("Enter your car uid:")
deleteCar(uid)