Skip to content

Commit c83f2a3

Browse files
committed
✨ (Flask-Smorest) Add start/end code for lectures in this section
1 parent 795f43b commit c83f2a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1698
-42
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
title: "Improvements to our first REST API"
3+
description: "Let's add a few routes to our first REST API, so it better matches what a production REST API would look like."
4+
---
5+
6+
# Improvements to our first REST API
7+
8+
- [ ] Set metadata above
9+
- [ ] Start writing!
10+
- [ ] Create `start` folder
11+
- [ ] Create `end` folder
12+
- [ ] Write TL;DR
13+
- [ ] Create per-file diff between `end` and `start` (use "Compare Folders")
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: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import uuid
2+
from flask import Flask, request
3+
from flask_smorest import abort
4+
5+
from db import stores, items
6+
7+
8+
app = Flask(__name__)
9+
10+
11+
@app.get("/items/<string:id>")
12+
def get_item(item_id):
13+
try:
14+
return items[item_id]
15+
except KeyError:
16+
abort(404, message="Item not found.")
17+
18+
19+
@app.delete("/items/<string:id>")
20+
def delete_item(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+
28+
@app.put("/items/<string:id>")
29+
def update_item(item_id):
30+
item_data = request.get_json()
31+
# There's more validation to do here!
32+
# Like making sure price is a number, and also both items are optional
33+
# Difficult to do with an if statement...
34+
if "price" not in item_data or "name" not in item_data:
35+
abort(
36+
400,
37+
message="Bad request. Ensure 'price', and 'name' are included in the JSON payload.",
38+
)
39+
try:
40+
item = items[item_id]
41+
42+
# https://blog.teclado.com/python-dictionary-merge-update-operators/
43+
item |= item_data
44+
45+
return item
46+
except KeyError:
47+
abort(404, message="Item not found.")
48+
49+
50+
@app.get("/items")
51+
def get_all_items():
52+
return {"items": list(items.values())}
53+
54+
55+
@app.post("/items")
56+
def create_item():
57+
item_data = request.get_json()
58+
# Here not only we need to validate data exists,
59+
# But also what type of data. Price should be a float,
60+
# for example.
61+
if (
62+
"price" not in item_data
63+
or "store_id" not in item_data
64+
or "name" not in item_data
65+
):
66+
abort(
67+
400,
68+
message="Bad request. Ensure 'price', 'store_id', and 'name' are included in the JSON payload.",
69+
)
70+
for item in items.values():
71+
if (
72+
item_data["name"] == item["name"]
73+
and item_data["store_id"] == item["store_id"]
74+
):
75+
abort(400, message=f"Item already exists.")
76+
77+
item_id = uuid.uuid4().hex
78+
item = {**item_data, "id": item_id}
79+
items[item_id] = item
80+
81+
return item
82+
83+
84+
@app.get("/stores/<string:id>")
85+
def get_store(store_id):
86+
try:
87+
# You presumably would want to include the store's items here too
88+
# More on that when we look at databases
89+
return stores[store_id]
90+
except KeyError:
91+
abort(404, message="Store not found.")
92+
93+
94+
@app.delete("/stores/<string:id>")
95+
def delete_store(store_id):
96+
try:
97+
del stores[store_id]
98+
return {"message": "Store deleted."}
99+
except KeyError:
100+
abort(404, message="Store not found.")
101+
102+
103+
@app.get("/stores")
104+
def get_all_stores():
105+
return {"stores": list(stores.values())}
106+
107+
108+
@app.post("/stores")
109+
def create_store():
110+
store_data = request.get_json()
111+
if "name" not in store_data:
112+
abort(
113+
400,
114+
message="Bad request. Ensure 'name' is included in the JSON payload.",
115+
)
116+
for store in stores.values():
117+
if store_data["name"] == store["name"]:
118+
abort(400, message=f"Store already exists.")
119+
120+
store_id = uuid.uuid4().hex
121+
store = {**store_data, "id": store_id}
122+
stores[store_id] = store
123+
124+
return store
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
flask
2+
python-dotenv
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM python:3.10
2+
EXPOSE 5000
3+
WORKDIR /app
4+
RUN pip install flask
5+
COPY . .
6+
CMD ["flask", "run", "--host", "0.0.0.0"]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import uuid
2+
from flask import Flask, request
3+
4+
app = Flask(__name__)
5+
6+
stores = {}
7+
items = {}
8+
9+
10+
@app.get("/item/<string:id>")
11+
def get_item(id):
12+
try:
13+
return items[id]
14+
except KeyError:
15+
return {"message": "Item not found"}, 404
16+
17+
18+
@app.post("/items")
19+
def create_item():
20+
request_data = request.get_json()
21+
new_item_id = uuid.uuid4().hex
22+
new_item = {
23+
"name": request_data["name"],
24+
"price": request_data["price"],
25+
"store_id": request_data["store_id"],
26+
}
27+
items[new_item_id] = new_item
28+
return new_item
29+
30+
31+
@app.get("/items")
32+
def get_all_items():
33+
return {"items": list(items.value())}
34+
35+
36+
@app.get("/stores/<string:id>")
37+
def get_store(id):
38+
try:
39+
# Here you might also want to add the items in this store
40+
# We'll do that later on in the course
41+
return stores[id]
42+
except KeyError:
43+
return {"message": "Store not found"}, 404
44+
45+
46+
@app.post("/stores")
47+
def create_store():
48+
request_data = request.get_json()
49+
new_store_id = uuid.uuid4().hex
50+
new_store = {"id": new_store_id, "name": request_data["name"]}
51+
stores[new_store_id] = new_store
52+
return new_store, 201
53+
54+
55+
@app.get("/stores")
56+
def get_all_stores():
57+
return {"stores": list(stores.value())}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: How to use Blueprints and MethodViews
3+
description: Flask-Smorest MethodViews allow us to simplify API Resources by defining all methods that interact with the resource in one Python class.
4+
---
5+
6+
# How to use Flask-Smorest MethodViews and Blueprints
7+
8+
- [ ] Set metadata above
9+
- [ ] Start writing!
10+
- [ ] Create `start` folder
11+
- [ ] Create `end` folder
12+
- [ ] Write TL;DR
13+
- [ ] Create per-file diff between `end` and `start` (use "Compare Folders")
14+
15+
## Adding MethodViews and to our application
16+
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

0 commit comments

Comments
 (0)