🎥 Here's a video tutorial about creating Flask API's using this boilerplate.
Starting with the flast-rest-hello boilerplate, you can find an example API working with a database. All your application code should be written inside the ./src/
folder.
routes.py
file in the project already.src/routes.py
file, here is where you must code to add your endpoints.For a more detailed explanation, look for the tutorial inside the docs
folder.
⚠️ Make sure you have python 3.6+
(Here we have a step-by-step guide of how to install python), pipenv
(On this article, we explain what is pyenv and how to install pyenv) and Postgres
installed on your computer, then make sure Postgres is running, and run the following commands:
1$ pipenv install # (to install pip packages) 2$ pipenv run migrate # (to create the database) 3$ pipenv run start # (to start the flask webserver)
⚠️ Local installation may work, but you can also try installing it online through 4Geeks' Click and Learn on top of Github Codespaces or Gitpod.
For each endpoint you will need to have:
@app
decorator that specifies the path for the endpoint.
<int:person_id>
methods=['PUT', 'GET']
get_single_person
).if request.method == 'PUT'
1@app.route('/person/<int:person_id>', methods=['PUT', 'GET']) 2def get_single_person(person_id): 3 """ 4 Single person 5 """ 6 body = request.get_json() #{ 'username': 'new_username'} 7 if request.method == 'PUT': 8 user1 = Person.query.get(person_id) 9 user1.username = body.username 10 db.session.commit() 11 return jsonify(user1.serialize()), 200 12 if request.method == 'GET': 13 user1 = Person.query.get(person_id) 14 return jsonify(user1.serialize()), 200 15 16 return "Invalid Method", 404
Let's say a request is coming from the client and we need to make sure it contains the right information.
We have to use conditionals to make the validations, if we want to validate the request body we have to retrieve it first and then add the condition, like this:
1body = request.get_json() 2if 'username' not in body: 3 raise APIException('You need to specify the username', status_code=400)
1@app.route('/person', methods=['POST']) 2def handle_person(): 3 4 # First we get the payload json 5 body = request.get_json() 6 7 if body is None: 8 raise APIException("You need to specify the request body as a json object", status_code=400) 9 if 'username' not in body: 10 raise APIException('You need to specify the username', status_code=400) 11 if 'email' not in body: 12 raise APIException('You need to specify the email', status_code=400) 13 14 # at this point, all data has been validated, we can proceed to insert into the database 15 user1 = Person(username=body['username'], email=body['email']) 16 db.session.add(user1) 17 db.session.commit() 18 return "ok", 200
The Flask boilerplate comes with a Postgres database installed and running, take 6 min to watch this video about Postgres.
We also use SQLAlchemy to abstract our database, that means that you don't have to write SQL to deal with your database, everything will be done using python.
Since we are not going to be using SQL directly, instead we are going to be working with SQLAlchemy using Python.
Click in this link for a more in-depth tutorial on how to work with SQLAlchemy and Postgres to CRUD (Create, Read, Update and Delete data).
Any API developed using this boilerplate will come with a quick and easy UI called: Flask Admin
.
The flask admin allows you to quickly see, add, delete or update information from your database tables.
You can access your flask admin by adding /admin
to the end of your API Host, for example:
If your API host is https://8000-blabla-us33.gitpod.io
then you can access your database admin like this: https://8000-blabla-us33.gitpod.io/admin
🎥 Here is an 8 min video explaining the Flask Admin.
With just a couple lines of code you can integrate your model into the Flask Admin, for example, if you have a Car
model you can add the model into the admin like this:
1from models import Car 2... 3admin.add_view(ModelView(Car, db.session))
But you have to add those two lines inside the admin.py
file like this:
1from flask_admin.contrib.sqla import ModelView 2from flask_admin import Admin 3from models import db, Car # < ------ Import the model 4 5def setup_admin(app): 6 admin = Admin(app, name='your_admin_name', template_mode='bootstrap3') 7 admin.add_view(ModelView(Car, db.session)) # < ------ Add the model to the admin
You can add as many models as you want, like this:
1from flask_admin.contrib.sqla import ModelView 2from flask_admin import Admin 3from models import db, Car, Person, Patient # < ------ Import the model 4 5def setup_admin(app): 6 admin = Admin(app, name='your_admin_name', template_mode='bootstrap3') 7 admin.add_view(ModelView(Car, db.session)) # < ------ Add the model to the admin 8 admin.add_view(ModelView(Person, db.session)) # < ------ Add the model to the admin 9 admin.add_view(ModelView(Patient, db.session)) # < ------ Add the model to the admin
Database changes are tracked using alembic migrations. You have to migrate and upgrade the migrations for every update you make to your models that must be reflected in the tables structure:
1$ pipenv run migrate #(to make the migrations) 2$ pipenv run upgrade #(to update your database with the migrations)
Sometimes the migration folder can get messed up, it's really hard to fix some of the issues and, since we are still in development, it makes sense to reset the entire database and migrations.
This boilerplate contains a script that can help you to reset from scratch all your database in case you need it. To run it execute pipenv run reset_db
, this will delete your entire database and rebuild it from the ground up, loosing all the data in the process. These are the actions that the script performs:
⚠️ Warning: your data will be lost
rm -R -f ./migrations
.flask db init
dropdb -h localhost -U gitpod example
createdb -h localhost -U gitpod example";
psql -h localhost example -U gitpod -c 'CREATE EXTENSION unaccent;'
pipenv run migrate
pipenv run upgrade
⚠️ Note: Please remember, all your data will be lost.
As an example, we are going to create a small API to manage a Person.
For each model
you will have to declare a class with the model properties and a method serialize
that returns a dictionary representation of the class:
1class Person(db.Model): 2 id = db.Column(db.Integer, primary_key=True) 3 username = db.Column(db.String(80), unique=True, nullable=False) 4 email = db.Column(db.String(120), unique=True, nullable=False) 5 6 # tell python how to print the class object on the console 7 def __repr__(self): 8 return '<Person %r>' % self.username 9 10 # tell python how to convert the class object into a dictionary ready to jsonify 11 def serialize(self): 12 return { 13 "username": self.username, 14 "email": self.email 15 }
📝 You can find more information on creating models here.
There are many ways to manipulate databases, but we decided to use Python and SQLAlchemy to do so. This means that you need no SQL knowledge, but we strongly recommend you still practice and master SQL for debugging purposes (most of the errors are shown in SQL language)
Assuming you have a Person object in your models.py
file.
1# get all the people 2people_query = Person.query.all() 3 4# get only the ones named "Joe" 5people_query = Person.query.filter_by(name='Joe') 6 7# map the results and your list of people inside of the all_people variable 8all_people = list(map(lambda x: x.serialize(), people_query)) 9 10# get just one person 11user1 = Person.query.get(person_id)
Assuming you have a Person object in your models.py
file.
1user1 = Person(username="my_super_username", email="my_super@email.com") 2db.session.add(user1) 3db.session.commit()
1user1 = Person.query.get(person_id) 2if user1 is None: 3 raise APIException('User not found', status_code=404) 4 5if "username" in body: 6 user1.username = body["username"] 7if "email" in body: 8 user1.email = body["email"] 9db.session.commit()
1user1 = Person.query.get(person_id) 2if user1 is None: 3 raise APIException('User not found', status_code=404) 4db.session.delete(user1) 5db.session.commit()
In this article you will find the details about how to implement this schema on your Flask API
This template is 100% compatible with Heroku and Render.com, just make sure to read the quick deployment guides.