Focus on deploying a simple Flask Application into Heroku, interacting with PostgreSQL and Troubleshooting | by Xue Wang | Towards Data Science

Flask is the tool that can be used to create API server. It is a micro-framework, which means that its core functionality is kept simple, but there are numerous extensions to allow developers to add other functionality (such as authentication and database support).
Heroku is a cloud platform where developers host applications, databases, and other services in several languages. Developers can use Heroku to deploy, manage, and scale applications. Heroku is also free, with paid specialized memberships, and most services such as a database offer a free tier.
Remark:
This story will focus on application deployment and database interactive without frontend and authentication. A tested sample application will be provided, so you don’t need to test it locally.
Let’s start.
First, the dependency: is based on my local setting as below:
python 3.8 (or higher than python3)PostgreSQL client and server are installed in WindowsUbuntu (Windows subsystem): Ubuntu 20.04.2 LTSPostgreSQL client and server are installed in UbuntuHeroku CLI
python 3.8 (or higher than python3)
PostgreSQL client and server are installed in Windows
Ubuntu (Windows subsystem): Ubuntu 20.04.2 LTS
PostgreSQL client and server are installed in Ubuntu
Heroku CLI
You can use the below sample for the deployment.
Before we start, create a database in PostgreSQL, give the name, no further action needed.
create databse local_db_name;
Create below 6 files that are needed for the application
models.py which defines the PostgreSQL database, table, and the configuration
models.py which defines the PostgreSQL database, table, and the configuration
import osfrom sqlalchemy import Column, String, Integer, create_enginefrom flask_sqlalchemy import SQLAlchemydb = SQLAlchemy()'''setup_db(app):    binds a flask application and a SQLAlchemy service'''def setup_db(app):    database_name ='local_db_name'    default_database_path= "postgres://{}:{}@{}/{}".format('postgres', 'password', 'localhost:5432', database_name)    database_path = os.getenv('DATABASE_URL', default_database_path)    app.config["SQLALCHEMY_DATABASE_URI"] = database_path    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False    db.app = app    db.init_app(app)'''    drops the database tables and starts fresh    can be used to initialize a clean database'''def db_drop_and_create_all():    db.drop_all()    db.create_all()class Movie(db.Model):    __tablename__ = 'movies'    id = Column(Integer, primary_key=True)    title = Column(String(80), unique=True)    release_date = Column(db.DateTime)    def __init__(self, title, release_date):        self.title = title        self.release_date = release_date    def details(self):        return {            'id': self.id,            'title': self.title,            'release_date': self.release_date,        }    def insert(self):        db.session.add(self)        db.session.commit()    def delete(self):        db.session.delete(self)        db.session.commit()    def update(self):        db.session.commit()
There are two database paths in models.py:
default_data_path is the local PostgreSQL path for local test
DATABASE_URL is the Heroku database URL, which will be generated with Heroku command and saved in setup.sh file
With os.getenv(), if DATABASE_URL is empty, it will get default_data_path directly
2. app.py: the main application
import osfrom flask import Flask, request, abort, jsonifyfrom flask_cors import CORSfrom models import setup_db, Movie, db_drop_and_create_alldef create_app(test_config=None):    # create and configure the app    app = Flask(__name__)    setup_db(app)    CORS(app)    """ uncomment at the first time running the app """    db_drop_and_create_all()    @app.route('/', methods=['GET'])    def home():        return jsonify({'message': 'Hello,hello, World!'})    @app.route("/movies")    def get_movies():        try:            movies = Movie.query.order_by(Movie.release_date).all()            movie=[]            movie=[mov.release_date for mov in movies]            return jsonify(                {                    "success": True,                    "movie name": movie                }            ), 200        except:            abort(500)    @app.errorhandler(500)    def server_error(error):        return jsonify({            "success": False,            "error": 500,            "message": "server error"        }), 500    return appapp = create_app()if __name__ == '__main__':    port = int(os.environ.get("PORT",5000))    app.run(host='127.0.0.1',port=port,debug=True)
3. manage.py: which can manage the database schema and changes that you make to it. With this, Heroku can run the migrations to the database hosted on the platform.
from flask_script import Managerfrom flask_migrate import Migrate, MigrateCommandfrom app import appfrom models import dbmigrate = Migrate(app, db)manager = Manager(app)manager.add_command('db', MigrateCommand)if __name__ == '__main__':    manager.run()
4. Procfile file: specifies the commands that are executed by the app on startup.
web: gunicorn app:app
5. requirements.txt file to include all of the dependencies.
angles==2.0certifi==2020.4.5.1chardet==3.0.4Flask==1.1.2Flask-Cors==3.0.8Flask-Migrate==2.5.3Flask-Script==2.0.6Flask-SQLAlchemy==2.4.1Jinja2==2.11.2jose==1.0.0Mako==1.1.2MarkupSafe==1.1.1psycopg2-binary==2.8.5pyasn1==0.4.8python-dateutil==2.8.1python-editor==1.0.4python-jose==3.1.0requests==2.23.0rsa==4.0six==1.14.0SQLAlchemy==1.3.16urllib3==1.25.9Werkzeug==1.0.1xacro==1.13.3gunicorn==20.0.4boto==2.49.0botocore==1.16.5
If the dependency not being installed yet, use the below to install locally. This is for the local run. For deploying in Heroku, Heroku will search remotely, not from your local end.
pip install -r requirements.txt
6. And we also need to create setup.sh file to save the environmental variable (DATABASE_URL), currently add one line and let it empty.
export DATABASE_URL = ''
Now the files should look like this:
For migration, run the below commands:
python3 manage.py db initpython3 manage.py db migratepython3 manage.py db upgrade
We can run our local migrations using our file, to mirror how Heroku will run behind the scenes for us when we deploy our application. In a local development environment, if there is any change in the database, I would like to use both migrate and upgrade commands, but in the production environment, in Heroku, migrate should be used at your local end, and then upgrade in Heroku.
Now the files should look like below:
The folders migrations and __pycache__ are generated automatically.
Now check the below folder:
migrations->versions
If it is empty, create an empty file called ‘.keep’ (or another name with the dot) as below:
If the version is empty, there will have an error during running git push command
manage.run()......FileNotFoundError: [Errno 2] No such file or directory: '/app/migrations/versions'
Supposed that Heroku CLI has been installed. Below commands are run in Ubuntu.
start to run Heroku commands
start to run Heroku commands
heroku login
It will prompt Heroku login page, click login in (supposed you have already signed up before), Ubuntu should automatically log you in and prompt another command line
2. Create Heroku app with the name casting-agency-xw
heroku create casting-agency-xw
3. Create a PostgreSQL database in Heroku: the only parameter you need to give is the application name. The database will be generated automatically with this command.
heroku addons:create heroku-postgresql:hobby-dev --app casting-agency-xw
4. Check the config
heroku config --app casting-agency-xw
You will find the DATABASE_URL as below:
=== casting-agency-xw Config Vars                                                                                                                                           DATABASE_URL: postgres://boozofewcboexi:fXXXXXX
Come back to the application setup.sh file, copy the DATABASE_URL from the previous step into setup.sh file as below
export DATABASE_URL='postgres://boozofewcboexi:fXXXXXX'
You can come to the below page in Heroku-deploy, using Heroku Git, as below codes, which is a bit different from the process from the website.
git initheroku git:clone -a casting-agency-xwgit add . git commit -am "add new files"git push heroku master
If everything is fine, the application is deployed as below:
But if you go to resource, click the Heroku-database, it will show 0 tables. Next, we need to migrate the database.
Migration the database
Run below command:
heroku run python3 manage.py db upgrade --app casting-agency-xw
You will see the table movies being added to the database, but it is empty. Next, let’s add a record to it.
Interactive with the database through Heroku psql
Make sure not only the client but also the server also being installed.
If try: psql version, the result is like below:
psql (PostgreSQL) 12.6 (Ubuntu 12.6-0ubuntu0.20.04.1)
but running psql return the error like below, it might be that the server hasn’t been installed.
psql: error: could not connect to server: No such file or directory          Is the server running locally and accepting                           connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
This link in StackOverflow might help you to install it.
Once the server is installed correctly, it should look like this:
Password:                                                         Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 4.4.0-18362-Microsoft x86_64)postgres=#
Now connect to the Heroku database using the below command:
heroku pg:psql postgresql-clear-05212 --app casting-agency-xw
postgresql-clear-05212 is the Heroku database name
Now insert two records:
Let’s check the endpoints:
Open Endpoint:
The get movies endpoint: /movies, it looks like:
Now delete one record with id =1:
The endpoint will look like this:
The result is the same as what we expect.
At first, deployment, uncomment db_drop_and_create_all() to generate the tables, but if you have made changes and want to git push the changes, do comment this command, as it will conflict with existing tables in Heroku.
At first, deployment, uncomment db_drop_and_create_all() to generate the tables, but if you have made changes and want to git push the changes, do comment this command, as it will conflict with existing tables in Heroku.
""" uncomment at the first time running the app """    db_drop_and_create_all()
2. If the deployment is ok, the database also works, but the error code 503, one possibility is that it might have previous traffic conflicted. Use below to scale dyno
heroku ps:scale web=0
Wait for a while (maybe 30 seconds), then try below:
heroku ps:scale web=1
2. If you need to update the table, run db migrate locally and upgrade on Heroku.
3. Understanding the table generated process:
init: create the alembic _versoin table, which is used internally by Alembic
migrate: check models.py and compare that to what is in your database actually.
The deployment might be various according to the local setting.
Thank you for your reading.
Reference:
Udacity full-stack web developer courses
Udacity full-stack web developer courses