# Python3 Flask Template A started template for Flask web apps using **Python 3.8**. This started template includes: - Dynamic frontend templates (not React) - Database support - Imperial LDAP user authentication - Multiple environments (development and production) - DoC Materials and Imperial Panopto API functionality This template is useful if you would like to create a backend REST API, optionally coupled with a simple dynamic frontend. ## Getting started Once you have created your new app, take a few minutes to look through the files to familiarise yourself with the project structure. - `app.py` : entry point to the Flask app - `templates/` : contains the frontend dynamic HTML files - `static/` : contains the static frontend assets (images, stylesheets and scripts) - `blueprints/` : contains all the application routes - `models/` : contains all the database models/schemas - `database/` : contains the database creation - `config/` : contains the app settings for the different environments **The first change you should make** is to set the `APP_NAME` variable in `config/config.py` to whatever you app's name is. To start the application locally, you can just run `python3 app.py` and this will launch the app on port 5000 (by default). You will notice a message in the console saying: `WARNING: Could not connect to the given database URL!` To fix this, you should set the environment variable DATABASE_URL accordingly. If you have PostgreSQL running locally, you can use that. Alternatively, you could use SQLite which is much simpler and does not require installation, for example, by running ```export DATABASE_URL="sqlite:///dev.db"``` If you do not want to use a database yet, you can ignore this warning and delete any routes that interact with the database. If you navigate to `http://localhost:5000`, you will see the response created by the route defined in `blueprints/home.py`. You will also notice the lines `Environment: production` and `Debug mode: off` when the Flask application starts in the console. To enable debug mode, you must set the environment variable `ENV` to `dev`, ie: `export ENV=dev` (see `config/config.py` for more details on different environments). ## Tutorial 1: Adding a new route In this template, we have provided several routes in the ```blueprints/``` directory. For example, we have provided all authentication-related routes (such as login and logout) in the ```auth.py```. It is good practice to group your routes logically and create new blueprints if necessary. To create a new blueprint, create a new file with the name of your blueprint and add: ```python from flask import Blueprint my_new_blueprint = Blueprint("new_bp", __name__, url_prefix="/new_bp") ``` You can then add a new route to that blueprint as follows: ```python @my_new_blueprint.route("/hello") def hello(): return "Hello!" ``` Finally and importantly, you must **register the blueprint** with the Flask application. To do this, go to ```app.py``` and register the blueprint in the same style the existing code does so. Your new route will now be accessible at ```/new_bp/hello```. ## Tutorial 2: Adding database interaction An important part of web app development is adding database support. If you ticked the "Include DB" option when creating the app, you will have automatically received a PostgreSQL database. You can get the connection string on the dashboard and connecting to the database using it to verify that you do indeed have a database. For local development, you should either use an SQLite db or install PostgreSQL, and set the ```DATABASE_URL``` environment variable as explained in the Getting Started section. ### Defining database schemas When working with relational databases, you must define **schemas** (models) for your tables. For example, if you are developing a web app where users can write blog posts, you may want to define a schema for a blog post and a user. To do this, you can create new schemas/models in the ```models/``` directory and create a new file. In this template, we have an ```Entity``` model which has a primary key ID, a name which is a string and an age which is an int. For the blog example, let's create a schema for a blog post. Create a file called ```post.py``` and add: ```python from database.db import db class Post(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(120), unique=False, nullable=False) text = db.Column(db.Test, unique=False, nullable=False) ``` You then need to make sure the database will create the relevant tables in the database. Go to ```app.py``` and import your model here: ```python # Import all your database models just like below: with app.app_context(): from models.entity import Entity if app.config["SQLALCHEMY_DATABASE_URI"]: db.create_all() ``` For more on schemas using SQL-Alchemy, [read here](https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/). ### Querying the database You can now use your database to create, read, update or delete records using the SQL-Alchemy package. For example, if we want to create a new Post and add it to the database, we can do something like: ```python from database.db import db post = db.session.query(Post).filter_by(title="My article").first() new_post = Post(title="A new article!", text="This is my amazing article, I hope you enjoyed it.") db.session.add(new_post) db.session.commit() ``` For more examples, please read around the [SQL-Alchemy package](https://flask-sqlalchemy.palletsprojects.com/en/2.x/). ### Running migrations and updating schemas To migrate your production database, you should connect to it directly using the connection string available in the PaaS dashboard with a tool like [PgAdmin](https://www.pgadmin.org/) (or connecting to it using your terminal), and run the relevant queries there. ## Tutorial 3: Configuring a test environment Often, we want to test that our web application is working as expected by writing a series of tests. For a simple example, you can view the code in the ```tests/``` directory. Unit testing a web app can be difficult, so we can test simple behaviours such as checking that a particular route is working as expected. We can setup a dummy app using a test configuration, as demonstrated in the ```test_home.py``` file. You can add more tests in this directory by making new functions with the ```test_``` prefix. To run these tests, you can run ```python -m pytest tests``` from the root directory. You should always run these tests before pushing/deploying new code, as they can catch out bugs. In fact, the CI/CD pipeline will run the tests for you, meaning that if a test fails, the pipeline will stop and your code won't be deployed until you fix the code causing the tests to fail.