You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
7.8 KiB
Markdown

3 years ago
---
title: Flask that soup 🥥
description: Confy Flask setup on the Soupboat
categories:
- Python
- Web
git: https://git.xpub.nl/kamo/soup_app
date: 15/02/2022
---
This is a template for simple and scalable Flask apps on the Soupboat. One of the main feature of this setup is that it enables a seamless work between local development (namely your computer) and the Jupiter Lab environment in the Soupboat.
This approach proposes these features:
### Url prefix
The Soupboat has some problems with generated URLs, and when we developed the app for SI16 we had to put every link + '/soupboat/si16', that was more a hack than a real solution. This setup fix it with a middleware that intercepts every request and adds the prefix automatically.
### Easy debug
To develop directly in Jupiter means to stop and restart continously the app to see the changes. With this setup we can work in locale and push the results when we are happy with them.
### Modular
Taking advantages of the Flask Blueprint system and Python modules, we can write a legible structure for our app, and then go crazy inside the blueprints of each page or super specific modules. In this way the code remains readable, and the relations between the different components are rendered clearly.
## Guide
Create a folder for your project. This will be the root of the app. Create a virtual environment in the folder. Open the terminal and write:
`python3 -m venv venv`
This will create a venv folder after a while. Then activate the virtual environment.
`. venv/bin/activate`
In this way every package we will install will be in the scope of the virtual environment, and this helps to avoid conflicts between different versions and projects.
Now we can install Flask.
`pip3 install flask`
Once we have installed it, let's create a folder `soup_app` for the Flask application. Here we will put all the modules needed for the flask app. In this folder we can create a `__init__.py` file, in order to make the folder to be recognized as a Python package.
Here we will initialize a factory for our Flask application. Instead of writing the application directly, we will write a function to generate it depending of the different environemnts. This comes handy when we want to develop our project in locale and then push it in the Soupboat. For more about the concept of factory here's a nice series about design pattern:[Factory Method Pattern](https://www.youtube.com/watch?v=EcFVTgRHJLM).
The basic structure of the app is something like
```python
# __init__.py
# import os to create the folder for the app
import os
# import flask to init the app, and send_from_directory for the icon file
from flask import Flask, send_from_directory
# url_prefix module to work either in local and in the soupboat
from . import prefix
def create_app(test_config=None):
# Create and configure the Flask App
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY="dev",
)
# load the instance config, if it exists, when not testing
if test_config is None:
app.config.from_pyfile("config.py", silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# create an endpoint for the icon
@app.route("/favicon.ico")
def favicon():
return send_from_directory(
os.path.join(app.root_path, "static"),
"favicon.ico",
mimetype="image/vnd.microsoft.icon",
)
# here we can import all our blueprints for each endpoints
# home blueprint
from . import home
app.register_blueprint(home.bp)
# register the prefix middleware
# it takes the url prefix from an .env file!
app.wsgi_app = prefix.PrefixMiddleware(
app.wsgi_app, prefix=os.environ.get("URL_PREFIX", "")
)
# return the app
return app
```
The home blueprint can be something as easy as
```python
# home.py
from flask import Blueprint
bp = Blueprint("home", __name__, url_prefix="/")
@bp.route("/")
def home():
return 'Hello world'
```
While the prefix it's a bit more abstract: it takes every incoming request and prefix it.
```python
# prefix.py
class PrefixMiddleware(object):
def __init__(self, app, prefix=""):
self.app = app
self.prefix = prefix
def __call__(self, environ, start_response):
if environ["PATH_INFO"].startswith(self.prefix):
environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
environ["SCRIPT_NAME"] = self.prefix
return self.app(environ, start_response)
else:
start_response("404", [("Content-Type", "text/plain")])
return ["This url does not belong to the app.".encode()]
```
If you execute `flask run` from the terminal now it will complain that there is no flask app defined in the environment variables. We could fix it setting manually the name of the flask app directly in the terminal, but we should do that every time we start the project.
Hence we will install python-dotenv, a library to use a `.env` file to configure the application. We will use different .env files depending on where we will deploy the application: one for our local development and one for the environment in the soupboat.
`pip3 install python-dotenv`
Now we can create in the root of our application (the same folder with the venv and flask-soup directories) a .env file, that the application will recognize automatically.
The contents for the local development will be something like this:
```env
FLASK_APP=postit
FLASK_ENV=development
```
while in the soupboat we will add the url_prefix property:
```env
FLASK_APP=postit
FLASK_ENV=development
URL_PREFIX=/soupboat/flask-soup/
```
If you now run `flask run` the application will start at the address `localhost:5000`, and you can debug and play around with things.
There are a couple of other files we can add in the root directory:
A `setup.py` module, useful for installing all the dependencies we need for our project (at the moment flask and python-dotenv)
```python
# setup.py
from setuptools import find_packages, setup
setup(
name="soup_app",
version="1.0.0",
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=["flask", "python-dotenv"],
)
```
and a `config.py`, to fine tuning the different configurations for the application.
```python
# config.py
import os
class Config(object):
DEBUG = False
TESTING = False
URL_PREFIX = ""
class ProductionConfig(Config):
DEBUG = False
URL_PREFIX = os.environ.get("URL_PREFIX")
class DevelopmentConfig(Config):
ENV = "development"
DEVELOPMENT = True
DEBUG = True
```
The last file to create is a `.gitignore` one, in which we will indicate the files that git should ignore when committing our changes.
I'm using this template atm
```.gitignore
# .gitignore
venv/
*.pyc
__pycache__/
.ipynb_checkpoints
instance/
.pytest_cache/
.coverage
htmlcov/
dist/
build/
*.egg-info/
.env
```
Notice that the .env file is ignored from your commits, so you will need to create one manually in each environment you wanna work.
Now you are ready to push the project to a git repo. Create a new one and follow the steps to push your contents on there.
On the xpub git should be something like:
```
git init
git add .
git commit -m 'Init my soup app'
git remote add origin https://git.xpub.nl/your-username/your-repo.git
git push -u origin master
```
And if it doesn't complain nice we are ready to go on the soupboat (or wherever)
TODO: finish this
steps:
1. clone the repo in the soupboat
2. create a virtual environment in the cloned folder
3. activate it
4. pip3 install -e . (aka install the dependencies)
5. create a .env file with the prefix
6. setup nginx