Skip to content

Commit 795f43b

Browse files
committed
✨ (Flask-Smorest) Add project for Flask-Smorest section
This project includes items and stores, and uses Docker and Flask-Smorest. No user authentication and no tags. Items and stores don't have a relationship either, as no database is included in the project. This is purely to show how to use Flask-Smorest.
1 parent d711dee commit 795f43b

File tree

9 files changed

+184
-0
lines changed

9 files changed

+184
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FLASK_APP=app
2+
FLASK_ENV=development
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM python:3.10
2+
EXPOSE 5000
3+
WORKDIR /app
4+
COPY ./requirements.txt requirements.txt
5+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
6+
COPY . .
7+
CMD ["flask", "run", "--host", "0.0.0.0"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from flask import Flask
2+
from flask_smorest import Api
3+
4+
from resources.item import blp as ItemBlueprint
5+
from resources.store import blp as StoreBlueprint
6+
7+
8+
app = Flask(__name__)
9+
10+
app.config["PROPAGATE_EXCEPTIONS"] = True
11+
app.config["API_TITLE"] = "Stores REST API"
12+
app.config["API_VERSION"] = "v1"
13+
app.config["OPENAPI_VERSION"] = "3.0.2"
14+
app.config["OPENAPI_URL_PREFIX"] = "/"
15+
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/swagger-ui"
16+
app.config["OPENAPI_SWAGGER_UI_URL"] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/"
17+
18+
api = Api(app)
19+
20+
api.register_blueprint(ItemBlueprint)
21+
api.register_blueprint(StoreBlueprint)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
db.py
3+
---
4+
5+
Later on, this file will be replaced by SQLAlchemy. For now, it mimics a database.
6+
Our data storage is:
7+
- stores have a unique ID and a name
8+
- items have a unique ID, a name, a price, and a store ID.
9+
"""
10+
11+
stores = {}
12+
items = {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
flask
2+
flask-smorest
3+
python-dotenv
4+
marshmallow
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import uuid
2+
from flask.views import MethodView
3+
from flask_smorest import Blueprint, abort
4+
5+
from schemas import ItemSchema, ItemUpdateSchema
6+
from db import items
7+
8+
blp = Blueprint("Items", "items", description="Operations on items")
9+
10+
11+
@blp.route("/item/<string:item_id>")
12+
class Item(MethodView):
13+
@blp.response(200, ItemSchema)
14+
def get(self, item_id):
15+
try:
16+
return items[item_id]
17+
except KeyError:
18+
abort(404, message="Item not found.")
19+
20+
def delete(self, item_id):
21+
try:
22+
del items[item_id]
23+
return {"message": "Item deleted."}
24+
except KeyError:
25+
abort(404, message="Item not found.")
26+
27+
@blp.arguments(ItemUpdateSchema)
28+
@blp.response(200, ItemSchema)
29+
def put(self, item_data, item_id):
30+
try:
31+
item = items[item_id]
32+
33+
# https://blog.teclado.com/python-dictionary-merge-update-operators/
34+
item |= item_data
35+
36+
return item
37+
except KeyError:
38+
abort(404, message="Item not found.")
39+
40+
41+
@blp.route("/items")
42+
class ItemList(MethodView):
43+
@blp.response(200, ItemSchema(many=True))
44+
def get(self):
45+
return items.values()
46+
47+
@blp.arguments(ItemSchema)
48+
@blp.response(201, ItemSchema)
49+
def post(self, item_data):
50+
for item in items.values():
51+
if (
52+
item_data["name"] == item["name"]
53+
and item_data["store_id"] == item["store_id"]
54+
):
55+
abort(400, message=f"Item already exists.")
56+
57+
item_id = uuid.uuid4().hex
58+
item = {**item_data, "id": item_id}
59+
items[item_id] = item
60+
61+
return item
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import uuid
2+
from flask.views import MethodView
3+
from flask_smorest import Blueprint, abort
4+
from db import stores
5+
from schemas import StoreSchema
6+
7+
8+
blp = Blueprint("Stores", "stores", description="Operations on stores")
9+
10+
11+
@blp.route("/store/<string:store_id>")
12+
class Store(MethodView):
13+
@blp.response(200, StoreSchema)
14+
def get(cls, store_id):
15+
try:
16+
# You presumably would want to include the store's items here too
17+
# More on that when we look at databases
18+
return stores[store_id]
19+
except KeyError:
20+
abort(404, message="Store not found.")
21+
22+
def delete(cls, store_id):
23+
try:
24+
del stores[store_id]
25+
return {"message": "Store deleted."}
26+
except KeyError:
27+
abort(404, message="Store not found.")
28+
29+
30+
@blp.route("/stores")
31+
class StoreList(MethodView):
32+
@blp.response(200, StoreSchema(many=True))
33+
def get(cls):
34+
return stores.values()
35+
36+
@blp.arguments(StoreSchema)
37+
@blp.response(201, StoreSchema)
38+
def post(cls, store_data):
39+
for store in stores.values():
40+
if store_data["name"] == store["name"]:
41+
abort(400, message=f"Store already exists.")
42+
43+
store_id = uuid.uuid4().hex
44+
store = {**store_data, "id": store_id}
45+
stores[store_id] = store
46+
47+
return store
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from marshmallow import Schema, fields
2+
3+
4+
class ItemSchema(Schema):
5+
id = fields.Str(dump_only=True)
6+
name = fields.Str(required=True)
7+
price = fields.Float(required=True)
8+
store_id = fields.Int(required=True)
9+
10+
11+
class ItemWithoutStoreSchema(Schema):
12+
id = fields.Str(dump_only=True)
13+
name = fields.Str(required=True)
14+
price = fields.Float(required=True)
15+
16+
17+
class ItemUpdateSchema(Schema):
18+
name = fields.Str()
19+
price = fields.Float()
20+
21+
22+
class StoreSchema(Schema):
23+
id = fields.Str()
24+
name = fields.Str()
25+
26+
27+
class StoreWitoutItemsSchema(Schema):
28+
id = fields.Str()
29+
name = fields.Str()

0 commit comments

Comments
 (0)