Skip to main content

Data model improvements

Starting code from section 4

This is the "First REST API" project from Section 4:

app.py
from flask import Flask, request

app = Flask(__name__)

stores = [
{
"name": "My Store",
"items": [
{
"name": "Chair",
"price": 15.99
}
]
}
]

@app.get("/store") # http://127.0.0.1:5000/store
def get_stores():
return {"stores": stores}


@app.post("/store")
def create_store():
request_data = request.get_json()
new_store = {"name": request_data["name"], "items": []}
stores.append(new_store)
return new_store, 201


@app.post("/store/<string:name>/item")
def create_item(name):
request_data = request.get_json()
for store in stores:
if store["name"] == name:
new_item = {"name": request_data["name"], "price": request_data["price"]}
store["items"].append(new_item)
return new_item, 201
return {"message": "Store not found"}, 404


@app.get("/store/<string:name>")
def get_store(name):
for store in stores:
if store["name"] == name:
return store
return {"message": "Store not found"}, 404


@app.get("/store/<string:name>/item")
def get_item_in_store(name):
for store in stores:
if store["name"] == name:
return {"items": store["items"]}
return {"message": "Store not found"}, 404

New files

Let's start off by creating a requirements.txt file with all our dependencies:

requirements.txt
flask
flask-smorest
python-dotenv

We're adding flask-smorest to help us write REST APIs more easily, and generate documentation for us.

We're adding python-dotenv so it's easier for us to load environment variables and use the .flaskenv file.

Next, let's create the .flaskenv file:

.flaskenv
FLASK_APP=app
FLASK_DEBUG=True

If we have the python-dotenv library installed, when we run the flask run command, Flask will read the variables inside .flaskenv and use them to configure the Flask app.

The configuration that we'll do is to define the Flask app file (here, app.py). Then we'll also set the FLASK_DEBUG flag to True, which does a couple things:

  • Makes the app give us better error messages and return a traceback when we make requests if there's an error.
  • Sets the app reloading to true, so the app restarts when we make code changes

We don't want debug mode to be enabled in production (when we deploy our app), but while we're doing development it's definitely a time-saving tool!

Code improvements

Creating a database file

First of all, let's move our "database" to another file.

Create a db.py file with the following content:

db.py
stores = {}
items = {}

In the existing code we only have a stores list, so delete that from app.py. From now on we will be storing information about items and stores separately.

What is in each dictionary?

Each dictionary will closely mimic how a database works: a mapping of ID to data. So each dictionary will be something like this:

{
1: {
"name": "Chair",
"price": 17.99
},
2: {
"name": "Table",
"price": 180.50
}
}

This will make it much easier to retrieve a specific store or item, just by knowing its ID.

Then, import the stores and items variables from db.py in app.py:

app.py
from db import stores, items

Using stores and items in our API

Now let's make use of stores and items separately in our API.

get_store

Here are the changes we'll need to make:

app.py
@app.get("/store/<string:name>")
def get_store(name):
for store in stores:
if store["name"] == name:
return store
return {"message": "Store not found"}, 404

get_stores

app.py
@app.get("/store")
def get_stores():
return {"stores": stores}

create_store

app.py
@app.post("/store")
def create_store():
request_data = request.get_json()
new_store = {"name": request_data["name"], "items": []}
stores.append(new_store)
return new_store, 201

create_item

app.py
@app.post("/store/<string:name>/item")
def create_item(name):
request_data = request.get_json()
for store in stores:
if store["name"] == name:
new_item = {"name": request_data["name"], "price": request_data["price"]}
store["items"].append(new_item)
return new_item, 201
return {"message": "Store not found"}, 404

get_items (new)

This is not an endpoint we could easily make when we were working with a single stores list!

@app.get("/item")
def get_all_items():
return {"items": list(items.values())}

get_item_in_store

app.py
@app.get("/store/<string:name>/item")
def get_item_in_store(name):
for store in stores:
if store["name"] == name:
return {"items": store["items"]}
return {"message": "Store not found"}, 404