Agenda
We will write a Flask REST application and create a Docker setup for it. This Flask application communicates with a PostgreSQL database on the host.
Application
Create a directory for the project.
mkdir flask_polls
cd flask_polls
We have a PostgreSQL database running on the host machine. The database name is peewee_polls
. We will use peewee
as ORM to connect to this database. You could use SQLAlchemy or any other replacement for peewee.
We will keep database configuration in environment variable.
Let’s create a settings.py which will read the environment variables.
# settings.py
import os
DATABASE = {
'HOST': os.environ['DB_HOST'],
'PORT': os.environ['DB_PORT'],
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
}
Let’s create db.py which will connect to the database.
# db.py
from peewee import PostgresqlDatabase
from . import settings
db = PostgresqlDatabase(
settings.DATABASE['NAME'], user=settings.DATABASE['USER'], password=settings.DATABASE['PASSWORD'], host=settings.DATABASE['HOST'], port=settings.DATABASE['PORT']
)
Let’s add the ORM for question
table.
# models.py
from peewee import Model, CharField, DateField
from .db import db
class Question(Model):
question_text = CharField()
pub_date = DateField()
class Meta:
database = db
if db.table_exists('question') is False:
db.create_tables([Question])
Let’s write the Flask application now:
import json
from datetime import datetime
from flask import Flask, request
from .models import Question
app = Flask(__name__)
@app.route('/polls/questions/', methods=['GET', 'POST'])
def questions():
if request.method == 'POST':
question_text = request.form['question_text']
pub_date = datetime.strptime(request.form['pub_date'], '%Y-%m-%d')
question = Question(question_text=question_text, pub_date=pub_date)
question.save()
rep = {'question_text': question.question_text, 'id': question.id, 'pub_date': question.pub_date.strftime('%Y-%m-%d')}
return rep
elif request.method == 'GET':
questions = Question.select()
l = []
for question in questions:
l.append({'question_text': question.question_text, 'id': question.id, 'pub_date': question.pub_date.strftime('%Y-%m-%d')})
return json.dumps(l)
Since we are treating this directory as a package, so ensure there is ` __init__.py
file too.
touch __init__.py
Let’s create a requirements.txt
using which Docker will install the needed libraries:
Flask
peewee
psycopg2
Let’s add the Dockerfile for this setup:
FROM python:3
WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
RUN export FLASK_APP=app.py
CMD ["flask", "run", "--host=0.0.0.0", "--port=8000"]
We can build the image using:
docker build -t flask-polls .
We want to run a container using this image now. Since our database configuration is in environment variable, so we will have to create an env.list which holds env variables.
╰─$ cat env.list
DB_HOST=host.docker.internal
DB_PORT=5432
DB_NAME=peewee_polls
DB_USER=akshar
DB_PASSWORD=akshar
In our setup, we want Docker to connect to a db running on host, that’s why we have setup DB_HOST
as host.docker.internal
.
Let’ run the container:
docker run -p 8000:8000 --env-file env.list flask-polls
We have mappend port 8000 of docker container with port 8000 on localhost. We also passed the env.list file which running the container, this ensures that DB_HOST
, DB_PORT
etc. are available on the container.
Navigate to http://localhost:8000/polls/questions/
and you should be able to see the questions. As you haven’t created any question yet, so you would probably see it empty.
Let’s create a question using requests
.
In [1]: import requests
In [2]: data = {'question_text': 'Ba ba blue sheep?', 'pub_date': '2019-10-20'}
In [12]: resp = requests.post('http://localhost:8000/polls/questions/', data=data)
In [13]: resp.status_code
Out[13]: 200
In [14]: resp.content
Out[14]: b'{"id":4,"pub_date":"2019-10-20","question_text":"Ba ba blue sheep?"}\n'
Making a GET request to http://localhost:8000/polls/questions/ should show this question.
Adding Docker compose
Ideally docker-compose is used when an application has multiple services. But we can use it even if application has a single service.
Let’s modify the Dockerfile so it looks like:
FROM python:3
WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
Let’s create a file docker-compose.yml
with following contents:
version: '3'
services:
flask:
build: .
command: flask run --host=0.0.0.0 --port=8000
ports:
- 8000:8000
environment:
- FLASK_APP=app.py
env_file:
- env.list
The current docker-compose version is 3.
We have added one service called flask
. You could name it anything. We did several additional things:
- Provided a
build
key toflask
service. This is where docker daemon looks for Dockerfile and this decides the build context. - Provided a
command
key toflask
service. This is similar toCMD
you have in Dockerfile. - Exposed a port and mapped it to a port on host.
- Added some env variables using
environment
andenv_file
.
Start the service:
docker-compose up
Navigate to http://localhost:8000/polls/questions/
. Things should keep working as earlier.
Thank you for reading the Agiliq blog. This article was written by Akshar on Oct 18, 2018 in Flask , Docker .
You can subscribe ⚛ to our blog.
We love building amazing apps for web and mobile for our clients. If you are looking for development help, contact us today ✉.
Would you like to download 10+ free Django and Python books? Get them here