├── .gitignore ├── Dockerfile ├── README.md ├── app ├── app.py ├── requirements.txt └── serializers.py └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | 3 | # Compiled source # 4 | ################### 5 | *.com 6 | *.class 7 | *.dll 8 | *.exe 9 | *.o 10 | *.so 11 | *.pyc 12 | *.py.orig 13 | 14 | # Packages # 15 | ############ 16 | # it's better to unpack these files and commit the raw source 17 | # git has its own built in compression methods 18 | *.7z 19 | *.dmg 20 | *.gz 21 | *.iso 22 | *.jar 23 | *.rar 24 | *.tar 25 | *.zip 26 | 27 | # Logs and databases # 28 | ###################### 29 | *.log 30 | *.sqlite 31 | 32 | # OS generated files # 33 | ###################### 34 | .DS_Store 35 | .DS_Store? 36 | ._* 37 | .Spotlight-V100 38 | .Trashes 39 | Icon? 40 | ehthumbs.db 41 | Thumbs.db 42 | 43 | 44 | # project related # 45 | ############### 46 | /nbproject 47 | .project 48 | .settings 49 | .settings/ 50 | /.settings 51 | .buildpath 52 | .buildpath/ 53 | /.buildpath 54 | /uploads 55 | /uploads/products 56 | *.sublime-* 57 | .idea 58 | .idea/ 59 | *.codekit 60 | .codekit-cache 61 | config.codekit 62 | .sass-cache 63 | .vscode 64 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.4-alpine3.10 2 | RUN apk add --no-cache --virtual .build-deps g++ python3-dev libffi-dev openssl-dev && \ 3 | apk add --no-cache --update python3 && \ 4 | pip3 install --upgrade pip setuptools 5 | RUN pip3 install pendulum service_identity 6 | RUN mkdir /app 7 | WORKDIR /app 8 | RUN pip install PyMySQL 9 | COPY app/requirements.txt /app/requirements.txt 10 | RUN pip install -r requirements.txt 11 | COPY app /app 12 | ENTRYPOINT ["python"] 13 | CMD ["app.py"] 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask, Flask-Restful, SQLAlchemy in Docker 2 | 3 | A dockerized app built on top of Flask, Flask-Restful and SQLAlchemy in Python3 4 | 5 | ## Tutorial 6 | 7 | [Watch it on Youtube](https://youtu.be/DsDuI4LDLXo) 8 | 9 | ## Requirements 10 | 11 | - Docker 12 | - Docker Compose 13 | 14 | ## Credits 15 | 16 | - Mazhar Ahmed 17 | 18 | > Copyright (c) Oceanize Inc 19 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from flask import Flask 4 | from flask_restful import Resource, Api 5 | from flask_sqlalchemy import SQLAlchemy 6 | 7 | from serializers import AlchemyEncoder 8 | 9 | app = Flask(__name__) 10 | api = Api(app) 11 | 12 | app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}/{}'.format( 13 | os.getenv('DB_USER', 'flask'), 14 | os.getenv('DB_PASSWORD', ''), 15 | os.getenv('DB_HOST', 'mysql'), 16 | os.getenv('DB_NAME', 'flask') 17 | ) 18 | db = SQLAlchemy(app) 19 | 20 | 21 | class User(db.Model): 22 | id = db.Column(db.Integer, primary_key=True) 23 | username = db.Column(db.String(80), unique=True, nullable=False) 24 | email = db.Column(db.String(120), unique=True, nullable=False) 25 | 26 | def __repr__(self): 27 | return '' % self.username 28 | 29 | 30 | # create the DB on demand 31 | @app.before_first_request 32 | def create_tables(): 33 | db.create_all() 34 | 35 | 36 | class Index(Resource): 37 | def get(self): 38 | ret = [] 39 | res = User.query.all() 40 | for user in res: 41 | ret.append( 42 | { 43 | 'username': user.username, 44 | 'email': user.email 45 | } 46 | ) 47 | return ret, 200 48 | 49 | 50 | api.add_resource(Index, '/') 51 | 52 | if __name__ == '__main__': 53 | app.run(host="0.0.0.0", debug=False) 54 | -------------------------------------------------------------------------------- /app/requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==7.0.0 2 | Click==7.0 3 | Flask==1.1.1 4 | Flask-RESTful==0.3.7 5 | itsdangerous==1.1.0 6 | Jinja2==2.10.1 7 | MarkupSafe==1.1.1 8 | pytz==2019.2 9 | six==1.12.0 10 | Werkzeug==0.15.5 11 | flask-sqlalchemy==2.4.0 12 | -------------------------------------------------------------------------------- /app/serializers.py: -------------------------------------------------------------------------------- 1 | import json 2 | import datetime 3 | from sqlalchemy.ext.declarative import DeclarativeMeta 4 | 5 | 6 | class AlchemyEncoder(json.JSONEncoder): 7 | 8 | def default(self, obj): 9 | if isinstance(obj.__class__, DeclarativeMeta): 10 | # an SQLAlchemy class 11 | fields = {} 12 | for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']: 13 | data = obj.__getattribute__(field) 14 | try: 15 | if isinstance(data, datetime.date): 16 | fields[field] = data.__str__() 17 | else: 18 | # this will fail on non-encodable values, like other classes 19 | json.dumps(data) 20 | fields[field] = data 21 | except TypeError: 22 | fields[field] = None 23 | # a json-encodable dict 24 | return fields 25 | 26 | return json.JSONEncoder.default(self, obj) 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Author: Mazhar Ahmed, CTO, Oceanize Inc 2 | # All rights reserved. Copyright (c) Oceanize Inc 3 | version: '3' 4 | services: 5 | app: 6 | image: oceanize/flask_app:latest 7 | container_name: flask_app 8 | restart: unless-stopped 9 | labels: 10 | project: "flask" 11 | day: "9 Sep 2019" 12 | build: 13 | # build the image from Dockerfile 14 | context: . 15 | dockerfile: Dockerfile 16 | environment: 17 | # set database, memcached etc credentials 18 | DB_HOST: mysql 19 | DB_NAME: flask 20 | DB_USER: flask 21 | DB_PASSWORD: slimdingo85 22 | volumes: 23 | - ./app:/app 24 | links: 25 | # network connection with these services 26 | - mysql 27 | depends_on: 28 | # build those images first 29 | - mysql 30 | ports: 31 | # bind on our 5000 port because most people have 80, 8080 already bound 32 | - "5000:5000" 33 | 34 | mysql: 35 | container_name: flask_mysql 36 | # let's grab from mysql 5.7 image 37 | image: mysql:5.7 38 | volumes: 39 | # pass volume named mysql-data to mysql container 40 | - mysql-data:/var/lib/mysql 41 | restart: unless-stopped 42 | labels: 43 | project: "flask" 44 | day: "9 Sep 2019" 45 | # ports: 46 | # - "3306:3306" 47 | environment: 48 | # credentials 49 | MYSQL_ROOT_PASSWORD: slimdingo85 50 | MYSQL_DATABASE: flask 51 | MYSQL_USER: flask 52 | MYSQL_PASSWORD: slimdingo85 53 | 54 | phpmyadmin: 55 | depends_on: 56 | - mysql 57 | image: phpmyadmin/phpmyadmin:latest 58 | container_name: flask_phpmyadmin 59 | restart: unless-stopped 60 | labels: 61 | project: "flask" 62 | day: "9 Sep 2019" 63 | ports: 64 | - "5010:80" 65 | environment: 66 | PMA_HOST: mysql 67 | PMA_USER: flask 68 | PMA_PASSWORD: slimdingo85 69 | MYSQL_ROOT_PASSWORD: slimdingo85 70 | 71 | # volumes definition here 72 | volumes: 73 | # just a persistance data 74 | mysql-data: 75 | driver: local --------------------------------------------------------------------------------