Developing with Frontend & Backend Lesson
Notes and Hacks for Developing with Frontend & Backend Lesson
Introduction
Frontend and backend are two essential components of a web application. The frontend is the part of the application that interacts with the user, whereas the backend is the part that handles the logic and data processing behind the scenes.
The frontend, also known as the client-side, typically consists of HTML, CSS, and JavaScript code that runs in the user's web browser. The frontend handles the user interface, page layout, and overall look of the application. It also handles user interactions, such as submitting forms, clicking buttons, and navigating between pages.
On the other hand, the backend, also known as the server-side, typically consists of a server, a database, and, in our case, APIs. The backend handles the processing and storage of data, manages user authentication and authorization, and handles business logic and rules. The backend also communicates with the frontend, providing the necessary data to render the user interface and processing user inputs.
Backend
In our class we mainly use Python and SQL/JSON to create APIs and databases. Here is a simple example of creating a SQL database and using CRUD as well.
What is CRUD
-
C: The 'C' stands for create, meaning to create a new entry in a database. In this case, creating a new entry about a certain movie or TV show.
-
R: Read, or to retrieve data from the database. In this case it is selecting the movie/TV shwo that you choose to display.
-
U: Update, or changing an existing entry in the database. In this case it is selecting the preexisting movie/TV show and changing the values to match what you want.
-
D: Delete, or removing data from the database. In this case it is selecting the preexisting movie/TV show and removing the entry from the database.
Films API
This API is intended to be used as a list of movies and TV shows that a person has watched. It includes attributes for the Film name(key), the year released, the language, the number of episodes, A list of the number of episodes(using pickletype), and a youtube url for the trailer. The CRUD works as follows: Create: Enter the above mentioned attributes Read: Returns all of the films and their attributes Update: Takes in new episodes watched, and a list of their names, and adds them to their respective attibutes Delete: Option for deleting every film, also takes in a name to delete that film if it exists
from flask import Flask
import sqlite3
app = Flask(__name__)
# Connect to the SQLite database using SQLite3
conn = sqlite3.connect('films.db')
# Create a cursor object to execute SQL commands
cursor = conn.cursor()
# Create a table in the database
cursor.execute('''CREATE TABLE movies
(id INTEGER PRIMARY KEY, title TEXT, year INTEGER, epcount INTEGER, language TEXT, trailer TEXT, eplist TEXT)''')
# Commit the changes to the database and close the connection
conn.commit()
conn.close()
import sqlite3
def create():
# Ask the user for movie details
title = input("Enter the movie/tv show title: ")
year = input("Enter the movie/tv show release year: ")
epcount = input("Enter the movie/tv show epcount: ")
language = input("Enter the movie/tv show language: ")
eplist = input("Enter the movie/tv show episode names: ")
trailer = input("Enter the link movie/tv show trailer: ")
# Connect to the database and create a cursor to execute SQL commands
database = 'films.db'
connection = sqlite3.connect(database)
cursor = connection.cursor()
try:
# Execute SQL to insert record into db
cursor.execute("INSERT INTO movies (title, year, epcount, language, eplist, trailer) VALUES (?, ?, ?, ?, ?, ?)", (title, year, epcount, language, eplist, trailer))
# Commit the changes
connection.commit()
print(f"{title} has been added to the list of movies.")
except sqlite3.Error as error:
print("Error while inserting record:", error)
# Close cursor and connection
cursor.close()
connection.close()
create()
def read(id):
# Connect to the database and create a cursor to execute SQL commands
database = 'films.db'
connection = sqlite3.connect(database)
cursor = connection.cursor()
# Execute SQL to select a record from db by id
cursor.execute("SELECT * FROM movies WHERE id=?", (id,))
# Fetch the record from the cursor
movie = cursor.fetchone()
# If movie exists, print its details, else print message
if movie:
print(f"{movie[0]}. {movie[1]}, {movie[2]}, {movie[3]}, {movie[4]}, {movie[5]}, {movie[6]}")
else:
print("Movie not found.")
# Close cursor and connection
cursor.close()
connection.close()
read(id=1)
def update(id):
# Connect to the database and create a cursor to execute SQL commands
database = 'films.db'
connection = sqlite3.connect(database)
cursor = connection.cursor()
# Ask the user for movie details to update
title = input("Enter the updated movie/tv show title: ")
year = input("Enter the updated movie/tv show release year: ")
epcount = input("Enter the updated movie/tv show epcount: ")
language = input("Enter the updated movie/tv show language: ")
eplist = input("Enter the updated movie/tv show episode names: ")
trailer = input("Enter the updated link movie/tv show trailer: ")
try:
# Execute SQL to update the record in db
cursor.execute("UPDATE movies SET title=?, year=?, epcount=?, language=?, eplist=?, trailer=? WHERE id=?", (title, year, epcount, language, eplist, trailer, id))
# Commit the changes
connection.commit()
print("Movie updated successfully.")
except sqlite3.Error as error:
print("Error while updating record:", error)
# Close cursor and connection
cursor.close()
connection.close()
update(id=1)
def delete(id):
# Connect to the database and create a cursor to execute SQL commands
database = 'films.db'
connection = sqlite3.connect(database)
cursor = connection.cursor()
try:
# Execute SQL to delete the record from db by id
cursor.execute("DELETE FROM movies WHERE id=?", (id,))
# Commit the changes
connection.commit()
print("Movie deleted successfully.")
except sqlite3.Error as error:
print("Error while deleting record:", error)
# Close cursor and connection
cursor.close()
connection.close()
delete(id=2)
Fetching
Overview
- Involves retrieving data from a server or database
- Can use different HTTP methods, such as GET, POST, PUT, and DELETE, to perform different types of operations on the server.
- Fetching can be done through a variety of ways including AJAX, XHR, and Axios
- In APCSP we tend to use the Fetch API over anything else
- Fetching involves sending a request to a server using a URL (Uniform Resource Locator), which identifies the location of the resource being requested.
- Can receive data in various formats, including JSON
- JSON data can be parsed into objects and arrays in JavaScript, making it easy to work with and manipulate in the frontend
import requests
url = "https://moviesdatabase.p.rapidapi.com/titles"
headers = {
"content-type": "application/octet-stream",
"X-RapidAPI-Key": "8401db6433msh3a46dd5bf23ad2ep19a280jsn48536a994246",
"X-RapidAPI-Host": "moviesdatabase.p.rapidapi.com"
}
response = requests.get(url, headers=headers)
print(response.json())
This is a functional fetch of a movies API from Rapid API, but the data isn't very readable. Below is an example of using Pandas to format the key values as a dataframe.
import requests
import pandas as pd
url = "https://moviesdatabase.p.rapidapi.com/titles"
headers = {
"content-type": "application/octet-stream",
"X-RapidAPI-Key": "8401db6433msh3a46dd5bf23ad2ep19a280jsn48536a994246",
"X-RapidAPI-Host": "moviesdatabase.p.rapidapi.com"
}
response = requests.get(url, headers=headers)
data = response.json()
# Create an empty DataFrame
df = pd.DataFrame()
# Extract the required information and store it in a list of dictionaries
results = data["results"]
entries = []
for result in results:
entry = {
"id": result["id"],
"title": result["titleText"]["text"],
"release_year": result["releaseYear"]["year"],
}
entries.append(entry)
# Convert the list of dictionaries into a DataFrame
df = pd.DataFrame(entries)
print(df)
# ADD YOUR OWN COLUMN TO THE DATAFRAME
Using Pandas to format a request obtained from a 3rd Party API makes it much easier to read and you can select what you want to display as well. Pandas makes it easy to access data that you feel is important.
- Create a completely unique API with all 4 CRUD features (Create, Read, Update, Delete)
- Create a Fetch API request for your corresponding API
- Attempt a complete website on GitHub Pages including HTML
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:///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)
import os, base64
import json
from sqlalchemy.exc import IntegrityError
# Define the User class to manage actions in the 'users' table
class Reader(db.Model):
__tablename__ = 'readers' # table name is plural, class name is singular
# Define the User schema with "vars" from object
id = db.Column(db.Integer, primary_key=True)
_username = db.Column(db.String(255), unique=True, nullable=False)
_name = db.Column(db.String(255), unique=False, nullable=False)
_book = db.Column(db.String(255), unique=False, nullable=False)
_year = db.Column(db.Integer, unique=False, nullable=False)
_rating = db.Column(db.Integer, unique=False, nullable=False)
# constructor of a User object, initializes the instance variables within object (self)
def __init__(self, username, name, book, year, rating):
self._username = username
self._name = name
self._book = book
self._year = year
self._rating = rating
# a name getter method, extracts username from object
@property
def username(self):
return self._username
# a setter function, allows username to be updated after initial object creation
@username.setter
def username(self, username):
self._username = username
# a name getter method, extracts name from object
@property
def name(self):
return self._name
# a setter function, allows name to be updated after initial object creation
@name.setter
def name(self, name):
self._name = name
# a getter method, extracts book from object
@property
def book(self):
return self._book
# a setter function, allows book to be updated after initial object creation
@book.setter
def book(self, book):
self._book = book
# a getter method, extracts year from object
@property
def year(self):
return self._year
# a setter function, allows year to be updated after initial object creation
@year.setter
def year(self, year):
self._year = year
# a getter method, extracts rating from object
@property
def rating(self):
return self._rating
# a setter function, allows book to be updated after initial object creation
@rating.setter
def rating(self, rating):
self._rating = rating
# 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,
"username": self.username,
"name": self.name,
"book": self.book,
"year": self.year,
"rating": self.rating
}
# CRUD update: updates user name, password, phone
# returns self
def update(self, username, name, book, year, rating):
"""only updates values with length"""
if len(username) > 0:
self.username = username
if len(name) > 0:
self.username = name
if len(book) > 0:
self.book = book
if year > 0:
self.year = year
if 0 < rating < 5:
self.rating = rating
db.session.commit()
return self
# CRUD delete: remove self
# None
def delete(self):
db.session.delete(self)
db.session.commit()
return None
"""Database Creation and Testing """
# Builds working data for testing
def initReaders():
with app.app_context():
"""Create database and tables"""
db.create_all()
"""Tester data for table"""
r1 = Reader(username="rey444", name="Shreya", book ="The Hunger Games", year=2019, rating=5)
r2 = Reader(username="jiyu", name="Jiya", book ="Divergent", year=2021, rating=3)
r3 = Reader(username="vai", name="Vaishavi", book ="Animal Farm", year=2020, rating=4)
readers = [r1, r2, r3]
"""Builds sample user/note(s) data"""
for reader in readers:
try:
reader.create()
print(f'Created user with username "{reader.username}".')
except IntegrityError:
'''fails with bad or duplicate data'''
db.session.remove()
print(f"Records exist, duplicate username, or error: {reader.username}")
import json
from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource # used for REST API building
user_api = Blueprint('reader_api', __name__,
url_prefix='/api/readers')
api = Api(user_api)
class ReaderAPI:
class _CRUD(Resource): # User API operation for Create, Read. THe Update, Delete methods need to be implemeented
def post(self): # Create method
''' Read data for json body '''
body = request.get_json()
''' Avoid garbage in, error checking '''
# validate username
username = body.get('username')
if username is None or len(username) < 1:
return {'message': f'Username is missing, or is less than a character'}, 400
# validate name
name = body.get('name')
if name is None or len(name) < 1:
return {'message': f'Name is missing, or is less than a character'}, 400
# validate book
book = body.get('book')
if book is None or len(book) < 1:
return {'message': f'Book name is missing, or is less than a character'}, 400
# validate year
year = body.get('year')
if year is None or year < 1900:
return {'message': f'Year is missing, or is before 1900'}, 400
# validate rating
rating = body.get('rating')
if rating is None or rating < 0:
return {'message': f'Book rating is missing, or is below zero'}, 400
elif rating is None or rating > 5:
return {'message': f'Book rating is missing, or is above five'}, 400
''' #1: Key code block, setup USER OBJECT '''
uo = Reader(username=username,
name=name,
book=book,
year=year,
rating=rating)
''' #2: Key Code block to add user to database '''
# create user in database
reader = uo.create()
# success returns json of user
if reader:
return jsonify(reader.read())
# failure returns error
return {'message': f'Processed {username}, either a format error or a duplicate'}, 400
def get(self): # Read Method
readers = Reader.query.all() # read/extract all users from database
json_ready = [reader.read() for reader in readers] # prepare output in json
return jsonify(json_ready) # jsonify creates Flask response object, more specific to APIs than json.dumps
def put(self):
body = request.get_json() # get the body of the request
id = body.get('id')
username = body.get('username')
name = body.get('name')
book = body.get('book') # get the book (Know what to reference)
year = body.get('year')
rating = body.get('rating')
reader = Reader.query.get(id) # get the player (using the uid in this case)
reader.update(username=username, name=name, book=book, year=year, rating=rating)
return f"{reader.read()} Updated"
def delete(self):
body = request.get_json()
id = body.get('id')
reader = Reader.query.get(id)
reader.delete()
return f"{reader.read()} Has been deleted"
# building RESTapi endpoint
api.add_resource(_CRUD, '/')
initReaders()
import sqlite3
readers = 'instance/sqlite.db' # this is location of database
def schema():
# Connect to the database file
conn = sqlite3.connect(readers)
# 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()
import sqlite3
def create():
username = input("Enter your username:")
name = input("Enter your name:")
book = input("Enter the name of the book you would like to log:")
year = input("Enter the year in which you read the book, in 'YYYY' format:")
rating = input("Enter your rating of the book using numbers 0-5, with 0 being the worst and 5 being the best:")
# Connect to the database file
conn = sqlite3.connect(readers)
# 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 readers (_username, _name, _book, _year, _rating) VALUES (?, ?, ?, ?, ?)", (username, name, book, year, rating))
# Commit the changes to the database
conn.commit()
print(f"A new user record {username} 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()
import sqlite3
def read():
# Connect to the database file
conn = sqlite3.connect(readers)
# 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 readers').fetchall()
# Print the results
if len(results) == 0: # results has data in it, so it could be an object; results has attributes
print("Table is empty")
else:
for row in results:
print(row)
# Close the cursor and connection objects
cursor.close()
conn.close()
read()
import sqlite3
def update():
username = input("Enter username to update")
book = input("Enter updated book name")
if len(book) < 2:
message = "invalid"
book = 'invalidbook'
else:
message = "successfully updated"
# Connect to the database file
conn = sqlite3.connect(readers)
# 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 readers SET _book = ? WHERE _username = ?", (book, username))
if cursor.rowcount == 0:
# The uid was not found in the table
print(f"No username {username} was not found in the table")
else:
print(f"The row with user id {username} the book 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()
import sqlite3
def delete():
username = input("Enter username to delete")
# Connect to the database file
conn = sqlite3.connect(readers)
# Create a cursor object to execute SQL commands
cursor = conn.cursor()
try:
cursor.execute("DELETE FROM readers WHERE _username = ?", (username,))
if cursor.rowcount == 0:
# The uid was not found in the table
print(f"No uid {username} 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 {username} 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()