Skip to content

Commit 1844a2f

Browse files
authored
Update example for latest version of SQLAlchemy (#2011)
Pass argument that disables SQLite thread check when creating the engine Revise request-processing functions to work within a database session context Drop TODO from the example README Co-authored-by: Christopher Lott <[email protected]>
1 parent 6e7dd39 commit 1844a2f

File tree

3 files changed

+51
-44
lines changed

3 files changed

+51
-44
lines changed

Diff for: examples/sqlalchemy/README.rst

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ SQLAlchemy Example
44

55
.. note::
66

7-
TODO: Update this example to work with recent (2024) versions of Python and SQLAlchemy.
8-
97
A simple example of how one might use SQLAlchemy as a backing store for a
108
Connexion based application.
119

Diff for: examples/sqlalchemy/app.py

100755100644
+37-35
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,61 @@
55
import orm
66
from connexion import NoContent
77

8-
db_session = None
9-
108

119
def get_pets(limit, animal_type=None):
12-
q = db_session.query(orm.Pet)
13-
if animal_type:
14-
q = q.filter(orm.Pet.animal_type == animal_type)
15-
return [p.dump() for p in q][:limit]
10+
with db_session_factory() as db_session:
11+
q = db_session.query(orm.Pet)
12+
if animal_type:
13+
q = q.filter(orm.Pet.animal_type == animal_type)
14+
return [p.dump() for p in q][:limit]
1615

1716

1817
def get_pet(pet_id):
19-
pet = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
20-
return pet.dump() if pet is not None else ("Not found", 404)
18+
with db_session_factory() as db_session:
19+
pet = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
20+
return pet.dump() if pet is not None else ("Not found", 404)
2121

2222

2323
def put_pet(pet_id, pet):
24-
p = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
25-
pet["id"] = pet_id
26-
if p is not None:
27-
logging.info("Updating pet %s..", pet_id)
28-
p.update(**pet)
29-
else:
30-
logging.info("Creating pet %s..", pet_id)
31-
pet["created"] = datetime.datetime.utcnow()
32-
db_session.add(orm.Pet(**pet))
33-
db_session.commit()
34-
return NoContent, (200 if p is not None else 201)
24+
with db_session_factory() as db_session:
25+
p = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
26+
pet["id"] = pet_id
27+
if p is not None:
28+
logging.info("Updating pet %s..", pet_id)
29+
p.update(**pet)
30+
else:
31+
logging.info("Creating pet %s..", pet_id)
32+
pet["created"] = datetime.datetime.now(datetime.UTC)
33+
db_session.add(orm.Pet(**pet))
34+
db_session.commit()
35+
return NoContent, (200 if p is not None else 201)
3536

3637

3738
def delete_pet(pet_id):
38-
pet = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
39-
if pet is not None:
40-
logging.info("Deleting pet %s..", pet_id)
41-
db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).delete()
42-
db_session.commit()
43-
return NoContent, 204
44-
else:
45-
return NoContent, 404
39+
with db_session_factory() as db_session:
40+
pet = db_session.query(orm.Pet).filter(orm.Pet.id == pet_id).one_or_none()
41+
if pet is not None:
42+
logging.info("Deleting pet %s..", pet_id)
43+
db_session.delete(pet)
44+
db_session.commit()
45+
return NoContent, 204
46+
else:
47+
return NoContent, 404
4648

4749

4850
logging.basicConfig(level=logging.INFO)
49-
db_session = orm.init_db("sqlite:///:memory:")
51+
db_session_factory = orm.init_db()
52+
pets = {
53+
1: {"name": "Aldo", "animal_type": "cat"},
54+
2: {"name": "Bailey", "animal_type": "dog"},
55+
3: {"name": "Hugo", "animal_type": "cat"},
56+
}
57+
for id_, pet in pets.items():
58+
put_pet(id_, pet)
5059
app = connexion.FlaskApp(__name__, specification_dir="spec")
5160
app.add_api("openapi.yaml")
5261
app.add_api("swagger.yaml")
5362

54-
application = app.app
55-
56-
57-
@application.teardown_appcontext
58-
def shutdown_session(exception=None):
59-
db_session.remove()
60-
6163

6264
if __name__ == "__main__":
6365
app.run(port=8080, reload=False)

Diff for: examples/sqlalchemy/orm.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from sqlalchemy import Column, DateTime, String, create_engine
22
from sqlalchemy.ext.declarative import declarative_base
3-
from sqlalchemy.orm import scoped_session, sessionmaker
3+
from sqlalchemy.orm import sessionmaker
4+
from sqlalchemy.pool import StaticPool
45

56
Base = declarative_base()
67

@@ -24,11 +25,17 @@ def dump(self):
2425
return {k: v for k, v in vars(self).items() if not k.startswith("_")}
2526

2627

27-
def init_db(uri):
28-
engine = create_engine(uri, convert_unicode=True)
29-
db_session = scoped_session(
30-
sessionmaker(autocommit=False, autoflush=False, bind=engine)
28+
def init_db():
29+
"""
30+
Initialize the database and return a sessionmaker object.
31+
`check_same_thread` and `StaticPool` are helpful for unit testing of
32+
in-memory sqlite databases; they should not be used in production.
33+
https://stackoverflow.com/questions/6519546/scoped-sessionsessionmaker-or-plain-sessionmaker-in-sqlalchemy
34+
"""
35+
engine = create_engine(
36+
url="sqlite:///:memory:",
37+
connect_args={"check_same_thread": False},
38+
poolclass=StaticPool,
3139
)
32-
Base.query = db_session.query_property()
3340
Base.metadata.create_all(bind=engine)
34-
return db_session
41+
return sessionmaker(autocommit=False, autoflush=False, bind=engine)

0 commit comments

Comments
 (0)