This lab will cover a number of advanced features you can add to your flask applications.

We can add global error handlers to our flask application. Error handlers are fallback functions that would be executed when a runtime error occurs in a route.

@app.errorhandler(404)
def page_not_found(error):
    return jsonify({'error':f'{request.url} is not a valid endpoint on this API'}), 404

@app.errorhandler(Exception)
def page_not_found(error):
    if isinstance(error, HTTPException):
        return error
    return jsonify({'error':f'{error.name} {error.description}'}), 500

The first error handler is executed when a request is made to a url that has no route. The second error handler is a fallback to any error that occurs during a route, it returns a message and prevents the server from crashing.

Bridge Tables

Often we need to represent many to many relationships in our applications. A bridge/helper table is needed to store the associated objects. This table can be created with db.Table

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags, lazy='subquery',
        backref=db.backref('pages', lazy=True))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)


If the bridge table contains extra metadata about the connection, then an SQLAlchemy class should be used instead

class Listing(db.Model):
    listingId = db.Column(db.Integer, primary_key=True)
    userId = db.Column(db.Integer, db.ForeignKey('user.id'))
    gameId = db.Column(db.Integer, db.ForeignKey('game.gameId'))
    condition = db.Column(db.String)
    price = db.Column(db.Float)
    status = db.Column(db.String)# available, rented, delisted
    created = db.Column(db.DateTime, default=datetime.utcnow)
    rentals = db.relationship('Rental', backref=db.backref('listing', lazy='joined'))

Relationship Fields

The listing model above has a relationship field called rentals. Because the rentals model has a foreign key to listing we can create a field that would make all relevant rental objects accessible from a listing object.

class Rental(db.Model):
    rentalId = db.Column(db.Integer, primary_key=True)
    userId = db.Column(db.Integer, db.ForeignKey('user.id'))
    listingId = db.Column(db.Integer, db.ForeignKey('listing.listingId'))
    rental_date = db.Column(db.DateTime, default=datetime.utcnow)
    return_date = db.Column(db.DateTime, default=None)

The backref value will also make a relationship field in the child table (rental) so it can reference its corresponding listing.

Example:

listing = Listing.query.get(10)

# prints the rental dates of all rentals made for the listing
for rental in listing.rentals:
   print(rental.rental_date)

rental = Rental.query.get(11)
# prints the price of the listing that was rented
print(rental.listing.price)


A more indepth example can be found at the link below:

Game Rentals App Repo

Web Sockets is a web protocol that allows applications to have realtime two-way communication. It is typically used in chatting applications. SocketIO is a popular web socket library for adding real time connections to web applications.

The workspace below demonstrates Flask_SocketIO in a web app.

Flask Sockets with Persistent Messages

The SocketIO library must be set up on both the client side and server side of the application. The flask server has the socketIO package setup and is ready to receive socket connections.

app.py

index.html

Index.html has JS code to load the library and set up the connection to the same server the page is requested from ("/").

SocketIO works by defining event handlers for events which are emitted over the websocket connection. The handles must be defined on the client and server.

Here a chat event is sent to the server from index.html

The server receives the message and then broadcasts another chat event to all other connected clients that should receive the message based on the recipient specified.

app.py

The messages are also saved to the database so conversations can persist between sessions. The authenticated_only custom wrapper lets us identify which user is communicating based on their authenticated state.

The following pulls the conversation history of the room so that when either user logs on the previous messages are shown.

app.py

index.html

Uploading files is a very common feature in web applications. A file can be submitted in a HTML 5 form, received by the server and stored on the server's filesystem. However this can introduce potential security issues.

Additionally, files should be managed to avoid name clashes and metadata about the files should be stored in the database so that they can be retrieved.


You can view an example of this at the following workspace:

Flask Uploads

In order to render a table of uploaded files we need an upload model to keep track of the files uploaded. Additionally the files are renamed to a random filename to avoid name clashes.

Firstly we use a multipart form to send our file data to the server

index.html

Then our /upload route handles the form submission. The file data would have the key "file"

app.py

The route also has some error handling to assure a file is actually in the request and of an appropriate type.

models.py

When we construct an upload object, the store_file() function is called and writes the file to the disk with a random name.

uploads.py

The get_url() method in the upload model lets us render the uploaded image onto our template.

index.html

While the previous approach for uploads works it is by no means scalable because uploads are stored in the same location as the application code. Additionally, this would NOT work for applications deployed to services like heroku which cleans all app state when the dyno goes to sleep.

Dedicated storage services should be utilized in this use case as the uploads would be stored separately from application code. Hence we send the file to firebase storage for safe keeping instead.

index.html


In this version the firebase sdk is used to upload to the storage service, get the upload url, then pass it to the flask app so it can be tracked in the database.

Now our upload model simply stores the public url of the file that was sent to firebase storage and no longer writes to the disk of the server.

Flask Firebase Storage

Push notifications are also supported on the web. Using the firebase admin sdk and Firebase Cloud Messaging we can broadcast notifications to users.

This requires you to download a Google Service Account for your project so your web app can perform admin operations such as sending notifications.

Flask Notifications

Note the service_account.json file contains important secrets that should never be staged in a code repository.

For repl, these confidential values are stored in the repl secrets tab which then makes them available to the application code as environment variables.

There are two parts to this application which is explained as follows:

Notification Sender

The notification sender uses the firebase cloud messaging admin sdk to send notifications to users subscribed to a particular topic.

messenger.py contains all the setup for firebase cloud messaging and provides useful functions to our application such as send_to_topic() that lets us broadcast our notifications.

We simply need a route to invoke the function with parameters specified by a user.

app.py

There's also a route for subscribing users to topics.

app.py

Notification Receiver

The receiver is a static JS based application for receiving push notifications.

It is served by the following route in app.py.

app.py

The receiver has a significant amount of JS code for handling the various UI logic that is composed in script.js.

In order for push notifications to work even when the app isn't open a service-worker file must be added to the webpage and linked to the firebase sdk. Hence messaging-sw.js can be seen running on the page.

Finally messenger.js sets up firebase cloud messaging and provides functions to script.js for enabling notifications and setting up how notifications appear when the app is running in the foreground.

script.js

The subscribe form sends a request to the /subscribe route for subscribing the user to a particular notification topic using the fcm admin sdk on the server.

script.js

When a message is sent on the sender any subscribed receivers would receive a push notification as long as they were previously subscribed to the senders notification topic.

When open, the receiver app will show a toast message and render the message to the page. If the receiver app is not open the user would receive a push notification.

Hopefully this lab provides some useful resources to aid in your project implementation.

References & Additional Reading