Skip to content

Commit 64d2b8b

Browse files
committed
Add sample flask application.
1 parent 260fdba commit 64d2b8b

File tree

9 files changed

+191
-0
lines changed

9 files changed

+191
-0
lines changed

flask-app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.sqlite

flask-app/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Example Flask app
2+
3+
### Setting up
4+
1. Create a Python3 virtual environment and activate it.
5+
2. Install dependencies by running: `pip install -r requirements.txt`
6+
3. Initialize db by running: `python library_app/models.py`
7+
8+
### Running the app
9+
```python run.py```
10+
11+
### Guide to the code:
12+
1. The API requests are defined in views.py
13+
2. All the database models are defined in models.py

flask-app/library_app/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
Description: Setup the flask application.
3+
"""
4+
from flask import Flask
5+
from flask_sqlalchemy import SQLAlchemy
6+
7+
app = Flask(__name__)
8+
app.secret_key = 'dev'
9+
10+
# enter path to the sqlite file here. A new db is created if one does not exist.
11+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///library.sqlite'
12+
13+
db = SQLAlchemy(app)
14+
from library_app import models
15+
from library_app import views

flask-app/library_app/author.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Description: Functions related to Author
3+
"""
4+
from library_app import db
5+
from library_app.models import Author
6+
7+
8+
def get_all(filters):
9+
allowed_filters = ['name', 'country', 'year_of_birth']
10+
final_filters = {k: v for k, v in filters.items() if k in allowed_filters}
11+
authors = Author.query.filter_by(**final_filters).all()
12+
return authors
13+
14+
15+
def get(author_id):
16+
return Author.query.filter_by(id=author_id).first()
17+
18+
19+
def create(name, country=None, year_of_birth=None):
20+
new_author = Author(name=name, country=country, year_of_birth=year_of_birth)
21+
db.session.add(new_author)
22+
db.session.commit()
23+
24+
25+
def update(author_id, fields):
26+
result = Author.query.filter_by(id=author_id).update(fields)
27+
db.session.commit()
28+
return result
29+
30+
31+
def delete(author):
32+
db.session.delete(author)
33+
db.session.commit()

flask-app/library_app/models.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Description: Models: Classes representing DB tables.
3+
"""
4+
from library_app import db
5+
6+
from sqlalchemy.inspection import inspect
7+
8+
9+
class Serializer(object):
10+
"""Class for serializing SQLAlchemy objects into dicts."""
11+
12+
def serialize(self):
13+
return {c: getattr(self, c) for c in inspect(self).attrs.keys()}
14+
15+
@staticmethod
16+
def serialize_list(list_obj):
17+
return [m.serialize() for m in list_obj]
18+
19+
20+
class Author(db.Model, Serializer):
21+
# See http://flask-sqlalchemy.pocoo.org/2.0/models/#simple-example
22+
# for details on the column types.
23+
id = db.Column(db.Integer, primary_key=True)
24+
name = db.Column(db.String(120), nullable=False)
25+
country = db.Column(db.String(80))
26+
year_of_birth = db.Column(db.Integer)
27+
28+
def __repr__(self):
29+
return f'Author name:{self.name} country:{self.country} year: {self.year_of_birth}'
30+
31+
32+
if __name__ == '__main__':
33+
db.create_all()
34+
db.session.commit()

flask-app/library_app/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""
2+
Description: Util functions.
3+
"""
4+
5+
6+
def werkzeug_rule_endpoint(rule):
7+
tmp = []
8+
for is_dynamic, data in rule._trace:
9+
if is_dynamic:
10+
tmp.append(u"<%s>" % data)
11+
else:
12+
tmp.append(data)
13+
return repr((u"".join(tmp)).lstrip(u"|")).lstrip(u"u")

flask-app/library_app/views.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Description: Requests supported by the flask app.
3+
"""
4+
import sqlalchemy
5+
from flask import jsonify, request, make_response
6+
7+
from library_app import app, author, utils
8+
from library_app.models import Author
9+
10+
11+
@app.route('/')
12+
def index():
13+
return "Welcome to Library app"
14+
15+
16+
@app.route('/site-map')
17+
def site_map():
18+
urls = {utils.werkzeug_rule_endpoint(url): str(url.methods) for url in app.url_map.iter_rules()}
19+
return jsonify(urls)
20+
21+
22+
@app.route('/author', methods=['GET', 'POST'])
23+
def authors_view():
24+
if request.method == 'GET':
25+
authors = author.get_all(filters=request.args)
26+
return jsonify(data=Author.serialize_list(authors))
27+
28+
if request.method == 'POST':
29+
try:
30+
author.create(name=request.json['name'],
31+
country=request.json.get('country'),
32+
year_of_birth=request.json.get('year_of_birth'))
33+
return make_response(jsonify(response='OK'), 201)
34+
except sqlalchemy.exc.InvalidRequestError:
35+
return make_response(jsonify(error='Bad request'), 400)
36+
37+
38+
@app.route('/author/<int:author_id>', methods=['GET', 'PUT', 'PATCH', 'DELETE'])
39+
def author_view(author_id):
40+
author_result = author.get(author_id)
41+
if author_result is None:
42+
return make_response(jsonify(response=f'Author with id {author_id} not found'), 404)
43+
44+
if request.method == 'GET':
45+
return jsonify(author_result.serialize())
46+
47+
if request.method == 'DELETE':
48+
author.delete(author_result)
49+
return jsonify(response='OK')
50+
51+
if request.method in ['PUT', 'PATCH']:
52+
try:
53+
if request.method == 'PATCH':
54+
fields = request.json
55+
else: # for PUT request, use default values for empty fields
56+
fields = {'name': request.json['name'],
57+
'country': request.json.get('country'),
58+
'year_of_birth': request.json.get('year_of_birth')}
59+
author.update(author_id, fields)
60+
return jsonify(response='OK')
61+
62+
except (sqlalchemy.exc.InvalidRequestError, KeyError):
63+
return make_response(jsonify(error='Bad request'), 400)

flask-app/requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
click==7.1.2
2+
Flask==1.1.2
3+
Flask-SQLAlchemy==2.4.4
4+
itsdangerous==1.1.0
5+
Jinja2==2.11.2
6+
MarkupSafe==1.1.1
7+
SQLAlchemy==1.3.20
8+
Werkzeug==1.0.1

flask-app/run.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""
2+
Description: Script for starting the flask application. Usage: $ python run.py
3+
"""
4+
from library_app import app
5+
6+
7+
# If you want some code to not be executed when this file is imported, but only
8+
# when this file is run as "python filename.py", then put it under __name__=='__main__'.
9+
# More about this here: https://stackoverflow.com/a/419185
10+
if __name__ == '__main__':
11+
app.run(debug=True)

0 commit comments

Comments
 (0)