Skip to content

Commit 02f332b

Browse files
Initial commit
0 parents  commit 02f332b

14 files changed

+398
-0
lines changed

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Python Postgres Starter Pack
2+
3+
## Prerequisites
4+
1. Python 3.x
5+
2. Postgres database
6+
3. docker & docker-compose
7+
8+
## Configuration
9+
10+
Located under the yaml file.
11+
12+
- **SQLALCHEMY_DATABASE_URI** postgres db connection url
13+
14+
the config value can be set through environment variable **SQLALCHEMY_DATABASE_URI**.
15+
16+
## Local Running
17+
18+
Go to app folder.
19+
20+
Install requirements:
21+
22+
```bash
23+
pip install -r requirements.txt
24+
```
25+
26+
you can run flask migration to create table
27+
28+
```bash
29+
flask db init
30+
flask db migrate
31+
flask db upgrade
32+
```
33+
or run the db/init-scripts/ddl.sql to create table
34+
35+
36+
Run application
37+
38+
```bash
39+
python app.py
40+
```
41+
42+
or override with environment variable
43+
44+
```bash
45+
SQLALCHEMY_DATABASE_URI=xxx python app.py
46+
```
47+
48+
49+
## Docker compose Running
50+
51+
Run:
52+
```bash
53+
docker-compose up --build
54+
```
55+
56+
57+
## Test and Verification
58+
59+
Import the postman collection in docs folder to test and verify.

app/Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM python:3
2+
3+
# Copy app
4+
COPY . /app
5+
6+
WORKDIR /app
7+
8+
# Install dependenceis
9+
RUN pip install -r requirements.txt
10+
11+
# Expose port 5000
12+
EXPOSE 5000
13+
14+
# Run the server when container is launched
15+
CMD ["python", "app.py"]

app/app.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env python
2+
from config import app
3+
from controllers.user_controller import api
4+
5+
# register the api
6+
app.register_blueprint(api)
7+
8+
if __name__ == '__main__':
9+
''' run application '''
10+
app.run(host='0.0.0.0', port=5000)

app/config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
import os
3+
import yaml
4+
from flask import Flask
5+
from flask_sqlalchemy import SQLAlchemy
6+
from flask_migrate import Migrate
7+
8+
9+
app = Flask(__name__)
10+
11+
config_obj = yaml.load(open('config.yaml'), Loader=yaml.Loader)
12+
13+
# override the environment variables
14+
database_url = os.getenv('SQLALCHEMY_DATABASE_URI')
15+
app.config['SQLALCHEMY_DATABASE_URI'] = config_obj['SQLALCHEMY_DATABASE_URI'] if database_url is None else database_url
16+
17+
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
18+
19+
db = SQLAlchemy(app)
20+
21+
migrate = Migrate(app, db)

app/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#url: 'mysql://root@localhost:3306/sample_db?charset=utf8mb4'
2+
SQLALCHEMY_DATABASE_URI: 'postgresql://[email protected]:5433/testdb'

app/controllers/user_controller.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
from flask import Blueprint, jsonify, request
3+
import services.user_service as user_service
4+
from models.user import User
5+
from werkzeug.exceptions import HTTPException
6+
import json
7+
8+
api = Blueprint('users', 'users')
9+
10+
11+
@api.route('/users', methods=['GET'])
12+
def api_get():
13+
''' Get all entities'''
14+
users = user_service.get()
15+
return jsonify([user.as_dict() for user in users])
16+
17+
@api.route('/users', methods=['POST'])
18+
def api_post():
19+
''' Create entity'''
20+
user = user_service.post(request.json)
21+
return jsonify(user.as_dict())
22+
23+
@api.route('/users/<string:id>', methods=['PUT'])
24+
def api_put(id):
25+
''' Update entity by id'''
26+
body = request.json
27+
body['id'] = id
28+
res = user_service.put(body)
29+
return jsonify(res.as_dict()) if isinstance(res, User) else jsonify(res)
30+
31+
@api.route('/users/<string:id>', methods=['DELETE'])
32+
def api_delete(id):
33+
''' Delete entity by id'''
34+
res = user_service.delete(id)
35+
return jsonify(res)
36+
37+
38+
@api.errorhandler(HTTPException)
39+
def handle_exception(e):
40+
"""Return JSON format for HTTP errors."""
41+
# start with the correct headers and status code from the error
42+
response = e.get_response()
43+
# replace the body with JSON
44+
response.data = json.dumps({
45+
'success': False,
46+
"message": e.description
47+
})
48+
response.content_type = "application/json"
49+
return response

app/models/user.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python
2+
from flask import Flask, jsonify, request
3+
from config import db
4+
import yaml
5+
import os
6+
7+
8+
class User(db.Model):
9+
''' The data model'''
10+
# table name
11+
__tablename__ = 'users'
12+
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
13+
handle = db.Column(db.String(200), nullable=False)
14+
def as_dict(self):
15+
return {c.name: getattr(self, c.name) for c in self.__table__.columns}

app/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PyYAML==5.1.2
2+
Flask==1.1.1
3+
Flask-SQLAlchemy==2.4.1
4+
Flask-Migrate==2.5.2
5+
psycopg2==2.8.3

app/services/user_service.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python
2+
from models.user import User
3+
from config import db
4+
from werkzeug.exceptions import NotFound
5+
6+
def get():
7+
'''
8+
Get all entities
9+
:returns: all entity
10+
'''
11+
return User.query.all()
12+
13+
def post(body):
14+
'''
15+
Create entity with body
16+
:param body: request body
17+
:returns: the created entity
18+
'''
19+
user = User(**body)
20+
db.session.add(user)
21+
db.session.commit()
22+
return user
23+
24+
def put(body):
25+
'''
26+
Update entity by id
27+
:param body: request body
28+
:returns: the updated entity
29+
'''
30+
user = User.query.get(body['id'])
31+
if user:
32+
user = User(**body)
33+
db.session.merge(user)
34+
db.session.flush()
35+
db.session.commit()
36+
return user
37+
raise NotFound('no such entity found with id=' + str(body['id']))
38+
39+
def delete(id):
40+
'''
41+
Delete entity by id
42+
:param id: the entity id
43+
:returns: the response
44+
'''
45+
user = User.query.get(id)
46+
if user:
47+
db.session.delete(user)
48+
db.session.commit()
49+
return {'success': True}
50+
raise NotFound('no such entity found with id=' + str(id))
51+
52+
53+

db/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Create from official postgres image
2+
FROM postgres
3+
4+
# Add a database
5+
ENV POSTGRES_DB testdb
6+
7+
# Initial db scripts
8+
COPY ./init-scripts /docker-entrypoint-initdb.d/

db/init-scripts/ddl.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE public.users (
2+
id serial NOT NULL,
3+
handle varchar(128) NOT NULL,
4+
CONSTRAINT users_user_pkey PRIMARY KEY (id)
5+
);

docker-compose.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: '2'
2+
services:
3+
database:
4+
build: db
5+
restart: always
6+
environment:
7+
POSTGRES_DB: testdb
8+
POSTGRES_PASSWORD: password
9+
app:
10+
build: app
11+
depends_on: [database]
12+
links:
13+
- database
14+
ports:
15+
- "5000:5000"
16+
environment:
17+
SQLALCHEMY_DATABASE_URI: 'postgresql://postgres:password@database:5432/testdb'

0 commit comments

Comments
 (0)