Skip to main content

In depth: Store model

Code for models/store.py

from db import db


class StoreModel(db.Model):
__tablename__ = "stores"

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))

items = db.relationship("ItemModel", lazy="dynamic")

def __init__(self, name):
self.name = name

def json(self):
return {
"id": self.id,
"name": self.name,
"items": [item.json() for item in self.items.all()],
}

@classmethod
def find_by_name(cls, name):
return cls.query.filter_by(name=name).first()

@classmethod
def find_all(cls):
return cls.query.all()

def save_to_db(self):
db.session.add(self)
db.session.commit()

def delete_from_db(self):
db.session.delete(self)
db.session.commit()

SQLAlchemy relationships

This model is very similar to the ItemModel, but we see a new concept near the column definitions:

items = db.relationship("ItemModel", lazy="dynamic")

SQLAlchemy is smart enough to be able to construct a many-to-one relationship from this. Every item has a store_id property, so the items property of the StoreModel becomes a list of those items.

Or at least, it would if we remove lazy="dynamic".

With lazy="dynamic", items becomes a SQLAlchemy query, so whenever we want to access the items in the store we have to do something like this:

store = StoreModel.find_by_name("Amazon")
print(store.items.all())

That .all() is the key part here, and only needed when lazy="dynamic". Since store.items is a SQLAlchemy query, .all() makes it go into the database and retrieve all the items. We call this a "lazy" property because the items are not loaded until we do .all().

If we take away lazy="dynamic", then the items are loaded from the database as soon as the StoreModel object is created.

This can be quite powerful, because if we add a new item after the StoreModel object has been created, we can still see that the item is related to the store when we load from the database. Let's look at an example:

Adding an item to a store

Whenever an item is created and saved to the database with the correct store_id, SQLAlchemy can relate it back to the store. For example:

store = StoreModel("Amazon")
store.save_to_db() # This gives our store an 'id' since it's generated by the database

item = ItemModel("Chair", 12.99, 1)
item.save_to_db()

print(store.items.all()) # Will give us that Chair

Example: without lazy="dynamic"

Imagine we removed lazy="dynamic" from the db.relationship definition.

The code above would change to this (remove .all()):

store = StoreModel("Amazon")
store.save_to_db() # This gives our store an 'id' since it's generated by the database

item = ItemModel("Chair", 12.99, 1)
item.save_to_db()

print(store.items) # Will give us an empty list

And the code would no longer give us the new item, since it was created after the StoreModel object was created.