Files
sqlalchemy-alembic/Essential-SQLAlchemy-2nd.org
T
2026-09-25 15:26:05 +05:00

202 KiB
Raw Blame History

Essential SQLAlchemy (v1/v2)

Chapter 1: Schema and Types

SQLAlchemy v1

  import sqlalchemy
  print(sqlalchemy.__version__)
2.0.52
  from sqlalchemy import MetaData

  metadata = MetaData()
  from sqlalchemy import Table, Column, Integer, Numeric, String, CheckConstraint

  cookies = Table(
      "cookies",
      metadata,
      Column("cookie_id", Integer(), primary_key=True),
      Column("cookie_name", String(50), index=True),
      Column("cookie_recipie_url", String(255)),
      Column("cookie_sku", String(55)),
      Column("quantity", Integer()),
      Column("unit_cost", Numeric(12, 2)),
      CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
  )
  from datetime import datetime
  from sqlalchemy import DateTime, PrimaryKeyConstraint, UniqueConstraint

  users = Table(
      "users",
      metadata,
      Column("user_id", Integer()),
      Column("username", String(15), nullable=False),
      Column("email_address", String(255), nullable=False),
      Column("phone", String(20), nullable=False),
      Column("password", String(25), nullable=False),
      Column("created_on", DateTime(), default=datetime.now),
      Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      PrimaryKeyConstraint("user_id", name="user_pk"),
      UniqueConstraint("username", name="uix_username"),
  )
  @startuml
  skinparam linetype ortho
  skinparam nodesep 30
  skinparam ranksep 40

  skinparam entity {
  	BackgroundColor White
  	BorderColor Black
  	BorderThickness 1.2
  	FontStyle bold
  	FontSize 12
  }

  entity orders {
  	,**order_id** : PK
  	--
  	user_id : FK
  }
  entity line_items {
  	,**line_item_id**: PK
  	--
  	,**order_id** : FK
  	,**cookie_id** : FK
  	quantity
  	extended_cost
  }
  entity cookies {
  	,**cookie_id** : PK
  	--
  	cookie_name
  	cookie_reciepe_url
  	cookie_sku
  	quantity
  	unit_cost
  }
  entity users {
  	,**user_id** : PK
  	--
  	customer_number
  	username
  	email-address
  	phone
  	password
  	created_on
  	updated_on
  }

  orders ||--|{ line_items
  line_items }o--|| cookies
  orders }o--|| users

  orders -[hidden]right- line_items
  line_items -[hidden]right- cookies
  orders -[hidden]down- users
  @enduml

er.png

  from sqlalchemy import ForeignKey, Boolean

  orders = Table(
      "orders",
      metadata,
      Column("order_id", Integer(), primary_key=True),
      Column("user_id", ForeignKey("users.user_id")),
      Column("shipped", Boolean(), default=False),
  )


  line_items = Table(
      "line_items",
      metadata,
      Column("line_items_id", Integer(), primary_key=True),
      Column("order_id", ForeignKey("orders.order_id")),
      Column("cookie_id", ForeignKey("cookies.cookie_id")),
      Column("quantity", Integer()),
      Column("extended_cost", Numeric(12, 2)),
  )
  <<A1>>
  <<B1>>
  <<C1>>
  <<D1>>
  from sqlalchemy import create_engine

  engine = create_engine("sqlite:///:memory:", echo=True)
  metadata.create_all(engine)

SQLAlchemy v2

  #!/usr/bin/env python3
  from datetime import datetime
  from decimal import Decimal

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      DateTime,
      ForeignKey,
      Integer,
      Numeric,
      String,
      UniqueConstraint,
      create_engine,
  )
  from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship


  class Base(DeclarativeBase):
      """Базовый класс для всех ORM-моделей."""


  class Cookie(Base):
      __tablename__ = "cookies"

      cookie_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      cookie_name: Mapped[str | None] = mapped_column(String(50), index=True)
      cookie_recipie_url: Mapped[str | None] = mapped_column(String(255))
      cookie_sku: Mapped[str | None] = mapped_column(String(55))
      quantity: Mapped[int | None] = mapped_column(Integer)
      unit_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))

      __table_args__ = (CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),)

      # Связь: одна cookie -> много line_items
      line_items: Mapped[list["LineItem"]] = relationship(back_populates="cookie")

      def __repr__(self) -> str:
          return f"Cookie(cookie_id={self.cookie_id!r}, cookie_name={self.cookie_name!r})"


  class User(Base):
      __tablename__ = "users"

      user_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      username: Mapped[str] = mapped_column(String(15), nullable=False)
      email_address: Mapped[str] = mapped_column(String(255), nullable=False)
      phone: Mapped[str] = mapped_column(String(20), nullable=False)
      password: Mapped[str] = mapped_column(String(25), nullable=False)
      created_on: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.now)
      updated_on: Mapped[datetime | None] = mapped_column(
          DateTime, default=datetime.now, onupdate=datetime.now
      )

      __table_args__ = (UniqueConstraint("username", name="uix_username"),)

      # Связь: один user -> много orders
      orders: Mapped[list["Order"]] = relationship(back_populates="user")

      def __repr__(self) -> str:
          return f"User(user_id={self.user_id!r}, username={self.username!r})"


  class Order(Base):
      __tablename__ = "orders"

      order_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      user_id: Mapped[int | None] = mapped_column(ForeignKey("users.user_id"))
      shipped: Mapped[bool | None] = mapped_column(Boolean, default=False)

      # Связи
      user: Mapped["User | None"] = relationship(back_populates="orders")
      line_items: Mapped[list["LineItem"]] = relationship(back_populates="order")

      def __repr__(self) -> str:
          return f"Order(order_id={self.order_id!r}, user_id={self.user_id!r})"


  class LineItem(Base):
      __tablename__ = "line_items"

      line_items_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      order_id: Mapped[int | None] = mapped_column(ForeignKey("orders.order_id"))
      cookie_id: Mapped[int | None] = mapped_column(ForeignKey("cookies.cookie_id"))
      quantity: Mapped[int | None] = mapped_column(Integer)
      extended_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))

      # Связи
      order: Mapped["Order | None"] = relationship(back_populates="line_items")
      cookie: Mapped["Cookie | None"] = relationship(back_populates="line_items")

      def __repr__(self) -> str:
          return (
              f"LineItem(line_items_id={self.line_items_id!r}, "
              f"order_id={self.order_id!r}, cookie_id={self.cookie_id!r})"
          )


  if __name__ == "__main__":
      engine = create_engine("sqlite:///:memory:", echo=True)
      Base.metadata.create_all(engine)

Chapter 2: Working with Data via SQLAlchemy Core

SQLAlchemy v1

  from datetime import datetime

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      Column,
      DateTime,
      ForeignKey,
      Integer,
      MetaData,
      Numeric,
      PrimaryKeyConstraint,
      String,
      Table,
      UniqueConstraint,
      create_engine,
  )
  from sqlparse import format as sqlformat


  def sqlf(
      sql: str | object,
      reindent=True,
      keyword_case="upper",
      indent_width=2,
      strip_comments=False,
      identifier_case="lower",
  ):
      if not isinstance(sql, str):
          sql = str(sql)
      return sqlformat(
          sql,
          encoding="utf8",
          reindent=reindent,
          keyword_case=keyword_case,
          indent_width=indent_width,
          strip_comments=strip_comments,
          identifier_case=identifier_case,
      )


  metadata = MetaData()

  cookies = Table(
      "cookies",
      metadata,
      Column("cookie_id", Integer(), primary_key=True),
      Column("cookie_name", String(50), index=True),
      Column("cookie_recipe_url", String(255), unique=True),
      Column("cookie_sku", String(55), unique=True),
      Column("quantity", Integer()),
      Column("unit_cost", Numeric(12, 2)),
      CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
  )

  users = Table(
      "users",
      metadata,
      Column("user_id", Integer()),
      Column("username", String(15), nullable=False),
      Column("email_address", String(255), nullable=False),
      Column("phone", String(20), nullable=False),
      Column("password", String(25), nullable=False),
      Column("created_on", DateTime(), default=datetime.now),
      Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      PrimaryKeyConstraint("user_id", name="user_pk"),
      UniqueConstraint("username", name="uix_username"),
  )

  orders = Table(
      "orders",
      metadata,
      Column("order_id", Integer(), primary_key=True),
      Column("user_id", ForeignKey("users.user_id")),
      Column("shipped", Boolean(), default=False),
  )


  line_items = Table(
      "line_items",
      metadata,
      Column("line_items_id", Integer(), primary_key=True),
      Column("order_id", ForeignKey("orders.order_id")),
      Column("cookie_id", ForeignKey("cookies.cookie_id")),
      Column("quantity", Integer()),
      Column("extended_cost", Numeric(12, 2)),
  )

  engine = create_engine("sqlite:///:memory:", echo=True)
  connection = engine.connect()
  metadata.create_all(engine)

Insert Data

  ins = cookies.insert().values(
      cookie_name="chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
      cookie_sku="CC01",
      quantity="12",
      unit_cost="0.50"
  )
  sqlf(ins)
INSERT INTO cookies (cookie_name, cookie_recipe_url, cookie_sku, quantity, unit_cost)
VALUES (:cookie_name, :cookie_recipe_url, :cookie_sku, :quantity, :unit_cost)
  import json

  json.dumps(ins.compile().params, indent=2)
{
  "cookie_name": "chocolate chip",
  "cookie_recipe_url": "http://some.aweso.me/cookie/recipe.html",
  "cookie_sku": "CC01",
  "quantity": "12",
  "unit_cost": "0.50"
}
  result = connection.execute(ins)
  result.inserted_primary_key
1
  ins = cookies.insert()
  result = connection.execute(
      ins,
      dict(
          cookie_name="dark chocolate chip",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe_dark.html",
          cookie_sku="CC02",
          quantity=1,
          unit_cost=0.75,
      ),
  )
  result.inserted_primary_key
2
  from sqlalchemy import insert
  ins = insert(cookies).values(
      cookie_name="ligth chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe_light.html",
      cookie_sku="CC03",
      quantity=10,
      unit_cost=0.5
  )
  result = connection.execute(ins)
  result.inserted_primary_key
3
  ins = cookies.insert()
  inventory_list = [
      {
          "cookie_name": "peanut butter",
          "cookie_recipe_url": "http://some.aweso.me/cookie/panut.html",
          "cookie_sku": "PB01",
          "quantity": 24,
          "unit_cost": 0.25
      },
      {
          "cookie_name": "oatmeal raisin",
          "cookie_recipe_url": "http://some.okay.me/cookie/raisin.html",
          "cookie_sku": "EWW01",
          "quantity": 100,
          "unit_cost": 1.00
      }
  ]

  result = connection.execute(ins, inventory_list)
  result.inserted_primary_key_rows
None
None

Querying Data

  from sqlalchemy.sql import select

  s = select(cookies)
  # ResultProxy
  rp = connection.execute(s)
  rp.fetchall()
1 chocolate chip http://some.aweso.me/cookie/recipe.html CC01 12 Decimal (0.50)
2 dark chocolate chip http://some.aweso.me/cookie/recipe_dark.html CC02 1 Decimal (0.75)
3 ligth chocolate chip http://some.aweso.me/cookie/recipe_light.html CC03 10 Decimal (0.50)
4 peanut butter http://some.aweso.me/cookie/panut.html PB01 24 Decimal (0.25)
5 oatmeal raisin http://some.okay.me/cookie/raisin.html EWW01 100 Decimal (1.00)
  s = cookies.select()
  rp = connection.execute(s)
  results = rp.fetchall()
  results
1 chocolate chip http://some.aweso.me/cookie/recipe.html CC01 12 Decimal (0.50)
2 dark chocolate chip http://some.aweso.me/cookie/recipe_dark.html CC02 1 Decimal (0.75)
3 ligth chocolate chip http://some.aweso.me/cookie/recipe_light.html CC03 10 Decimal (0.50)
4 peanut butter http://some.aweso.me/cookie/panut.html PB01 24 Decimal (0.25)
5 oatmeal raisin http://some.okay.me/cookie/raisin.html EWW01 100 Decimal (1.00)
ResultProxy
  first_row = results[0]
  (
      first_row[1],
      first_row.cookie_name,
      # так не работает в SQLAlchemy 2
      #first_row[cookies.c.cookie_name],
      # так работает в SQLAlchemy 2
      first_row._mapping[cookies.c.cookie_name]
  )
chocolate chip chocolate chip chocolate chip
  rp = connection.execute(s)
  for record in rp:
      print(record.cookie_name)
2026-09-16 18:20:06,397 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_id, cookies.cookie_name, cookies.cookie_recipe_url, cookies.cookie_sku, cookies.quantity, cookies.unit_cost 
FROM cookies
2026-09-16 18:20:06,398 INFO sqlalchemy.engine.Engine [cached since 0.1297s ago] ()
chocolate chip
dark chocolate chip
ligth chocolate chip
peanut butter
oatmeal raisin
Controlling the Columns in the Query
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  rp = connection.execute(s)
  print(rp.keys())
  result = rp.first()
  print(result)
2026-09-16 18:20:06,420 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
2026-09-16 18:20:06,421 INFO sqlalchemy.engine.Engine [generated in 0.00045s] ()
RMKeyView(['cookie_name', 'quantity'])
('chocolate chip', 12)
Ordering
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(cookies.c.quantity)
  rp = connection.execute(s)
  for cookie in rp:
      print("{} - {}".format(cookie.quantity, cookie.cookie_name))
2026-09-16 18:20:06,443 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies ORDER BY cookies.quantity
2026-09-16 18:20:06,443 INFO sqlalchemy.engine.Engine [generated in 0.00017s] ()
1 - dark chocolate chip
10 - ligth chocolate chip
12 - chocolate chip
24 - peanut butter
100 - oatmeal raisin
  from sqlalchemy import desc
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(desc(cookies.c.quantity))
  rp = connection.execute(s)
  rp.fetchall()
oatmeal raisin 100
peanut butter 24
chocolate chip 12
ligth chocolate chip 10
dark chocolate chip 1
Limiting
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(cookies.c.quantity)
  s = s.limit(2)
  rp = connection.execute(s)
  print([result.cookie_name for result in rp])
2026-09-16 18:20:06,508 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies ORDER BY cookies.quantity
 LIMIT ? OFFSET ?
2026-09-16 18:20:06,512 INFO sqlalchemy.engine.Engine [generated in 0.00363s] (2, 0)
['dark chocolate chip', 'ligth chocolate chip']
Functions and Labels
  from sqlalchemy.sql import func

  s = select(func.sum(cookies.c.quantity))
  rp = connection.execute(s)
  print(rp.scalar())
2026-09-16 18:20:06,530 INFO sqlalchemy.engine.Engine SELECT sum(cookies.quantity) AS sum_1 
FROM cookies
2026-09-16 18:20:06,531 INFO sqlalchemy.engine.Engine [generated in 0.00018s] ()
147
  s = select(func.count(cookies.c.cookie_name))
  rp = connection.execute(s)
  record = rp.first()
  print(record._fields)
  print(record.count_1)
2026-09-16 18:20:06,548 INFO sqlalchemy.engine.Engine SELECT count(cookies.cookie_name) AS count_1 
FROM cookies
2026-09-16 18:20:06,548 INFO sqlalchemy.engine.Engine [generated in 0.00028s] ()
('count_1',)
5
  s = select(func.count(cookies.c.cookie_name).label('inventory_count'))
  rp = connection.execute(s)
  record = rp.first()
  print(record._fields)
  print(record.inventory_count)
2026-09-16 18:20:06,572 INFO sqlalchemy.engine.Engine SELECT count(cookies.cookie_name) AS inventory_count 
FROM cookies
2026-09-16 18:20:06,572 INFO sqlalchemy.engine.Engine [generated in 0.00027s] ()
('inventory_count',)
5
Filtering
  s = select(cookies).where(cookies.c.cookie_name == "chocolate chip")
  rp = connection.execute(s)
  record = rp.first()
  [
      (k, repr(v) if hasattr(v, "quantize") else v)
      for k, v in dict(record._mapping.items()).items()
  ]
cookie_id 1
cookie_name chocolate chip
cookie_recipe_url http://some.aweso.me/cookie/recipe.html
cookie_sku CC01
quantity 12
unit_cost Decimal('0.50')
  s = select(cookies).where(cookies.c.cookie_name.like("%chocolate%"))
  rp = connection.execute(s)
  for record in rp:
      print(record.cookie_name)
2026-09-16 18:20:06,635 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_id, cookies.cookie_name, cookies.cookie_recipe_url, cookies.cookie_sku, cookies.quantity, cookies.unit_cost 
FROM cookies 
WHERE cookies.cookie_name LIKE ?
2026-09-16 18:20:06,638 INFO sqlalchemy.engine.Engine [generated in 0.00392s] ('%chocolate%',)
chocolate chip
dark chocolate chip
ligth chocolate chip
  s = select(cookies.c.cookie_name, "SKU-" + cookies.c.cookie_sku)
  [row for row in connection.execute(s)]
chocolate chip SKU-CC01
dark chocolate chip SKU-CC02
ligth chocolate chip SKU-CC03
peanut butter SKU-PB01
oatmeal raisin SKU-EWW01
  from sqlalchemy import cast

  s = select(
      cookies.c.cookie_name,
      cast((cookies.c.quantity * cookies.c.unit_cost), Numeric(12, 2)).label("inv_cost"),
  )
  [(row.cookie_name, repr(row.inv_cost)) for row in connection.execute(s)]
chocolate chip Decimal('6.00')
dark chocolate chip Decimal('0.75')
ligth chocolate chip Decimal('5.00')
peanut butter Decimal('6.00')
oatmeal raisin Decimal('100.00')
Conjunctions
  from sqlalchemy import and_, or_, not_

  s = select(cookies).where(and_(cookies.c.quantity > 23, cookies.c.unit_cost < 0.40))
  [row.cookie_name for row in connection.execute(s)]
peanut butter
  s = select(cookies).where(
      or_(cookies.c.quantity.between(10, 50), cookies.c.cookie_name.contains("chip"))
  )
  [(row.cookie_name,) for row in connection.execute(s)]
chocolate chip
dark chocolate chip
ligth chocolate chip
peanut butter

Updating Data

  from sqlalchemy import update

  u = update(cookies).where(cookies.c.cookie_name == "chocolate chip")
  u = u.values(quantity=(cookies.c.quantity + 120))
  result = connection.execute(u)
  result.rowcount
1
  s = select(cookies).where(cookies.c.cookie_name == "chocolate chip")
  result = connection.execute(s).first()
  [
      (
          key,
          (
              repr(result._mapping[key])
              if hasattr(result._mapping[key], "quantize")
              else result._mapping[key]
          ),
      )
      for key in result._mapping.keys()
  ]
cookie_id 1
cookie_name chocolate chip
cookie_recipe_url http://some.aweso.me/cookie/recipe.html
cookie_sku CC01
quantity 132
unit_cost Decimal('0.50')

Deleting Data

  from sqlalchemy import delete

  u = delete(cookies).where(cookies.c.cookie_name == "dark chocolate chip")
  result = connection.execute(u)
  result.rowcount
1
  s = select(cookies).where(cookies.c.cookie_name == "dark chocolate chip")
  result = connection.execute(s).fetchall()
  len(result)
0

Joins

  customer_list = [
      {
          "username": "cookiemon",
          "email_address": "mon@cookie.com",
          "phone": "111-111-1111",
          "password": "password",
      },
      {
          "username": "cakeeater",
          "email_address": "cakeeater@cake.com",
          "phone": "222-222-2222",
          "password": "password",
      },
      {
          "username": "pieguy",
          "email_address": "guy@pie.com",
          "phone": "333-333-3333",
          "password": "password",
      },
  ]
  ins = users.insert()
  result = connection.execute(ins, customer_list)
  ins = insert(orders).values(user_id=1, order_id=1)
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {"order_id": 1, "cookie_id": 1, "quantity": 2, "extended_cost": 1.00},
      {"order_id": 1, "cookie_id": 3, "quantity": 12, "extended_cost": 3.00},
  ]
  result = connection.execute(ins, order_items)
  ins = insert(orders).values(user_id=2, order_id=2)
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {"order_id": 2, "cookie_id": 1, "quantity": 24, "extended_cost": 12.00},
      {"order_id": 2, "cookie_id": 4, "quantity": 6, "extended_cost": 6.00},
  ]
  result = connection.execute(ins, order_items)
  s = select(line_items)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
line_items_id order_id cookie_id quantity extended_cost
1 1 1 2 Decimal (1.00)
2 1 3 12 Decimal (3.00)
3 2 1 24 Decimal (12.00)
4 2 4 6 Decimal (6.00)
  s = select(orders)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
order_id user_id shipped
1 1 False
2 2 False
  s = select(users)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
user_id username email_address phone password created_on updated_on
1 cookiemon mon@cookie.com 111-111-1111 password datetime.datetime (2026 9 16 18 20 6 947339) datetime.datetime (2026 9 16 18 20 6 947349)
2 cakeeater cakeeater@cake.com 222-222-2222 password datetime.datetime (2026 9 16 18 20 6 947352) datetime.datetime (2026 9 16 18 20 6 947355)
3 pieguy guy@pie.com 333-333-3333 password datetime.datetime (2026 9 16 18 20 6 947359) datetime.datetime (2026 9 16 18 20 6 947360)
  columns = [
      orders.c.order_id,
      users.c.username,
      users.c.phone,
      cookies.c.cookie_name,
      line_items.c.quantity,
      line_items.c.extended_cost,
  ]

  cookiemon_orders = (
      select(*columns)
      .select_from(
          orders
          .join(users, orders.c.user_id == users.c.user_id)
          .join(line_items, orders.c.order_id == line_items.c.order_id)
          .join(cookies, line_items.c.cookie_id == cookies.c.cookie_id)
      )
      .where(users.c.username == "cookiemon")
  )
  result = connection.execute(cookiemon_orders)
  [row for row in result]
1 cookiemon 111-111-1111 chocolate chip 2 Decimal (1.00)
1 cookiemon 111-111-1111 ligth chocolate chip 12 Decimal (3.00)

SQLAlchemy v2

  from datetime import datetime

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      Column,
      DateTime,
      ForeignKey,
      Integer,
      MetaData,
      Numeric,
      PrimaryKeyConstraint,
      String,
      Table,
      UniqueConstraint,
      create_engine,
  )
  from sqlparse import format as sqlformat


  def sqlf(
      sql: str | object,
      reindent=True,
      keyword_case="upper",
      indent_width=2,
      strip_comments=False,
      identifier_case="lower",
  ):
      if not isinstance(sql, str):
          sql = str(sql)
      return sqlformat(
          sql,
          encoding="utf8",
          reindent=reindent,
          keyword_case=keyword_case,
          indent_width=indent_width,
          strip_comments=strip_comments,
          identifier_case=identifier_case,
      )


  metadata = MetaData()

  cookies = Table(
      "cookies",
      metadata,
      Column("cookie_id", Integer(), primary_key=True),
      Column("cookie_name", String(50), index=True),
      Column("cookie_recipe_url", String(255), unique=True),
      Column("cookie_sku", String(55), unique=True),
      Column("quantity", Integer()),
      Column("unit_cost", Numeric(12, 2)),
      CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
  )

  users = Table(
      "users",
      metadata,
      Column("user_id", Integer()),
      Column("username", String(15), nullable=False),
      Column("email_address", String(255), nullable=False),
      Column("phone", String(20), nullable=False),
      Column("password", String(25), nullable=False),
      Column("created_on", DateTime(), default=datetime.now),
      Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      PrimaryKeyConstraint("user_id", name="user_pk"),
      UniqueConstraint("username", name="uix_username"),
  )

  orders = Table(
      "orders",
      metadata,
      Column("order_id", Integer(), primary_key=True),
      Column("user_id", ForeignKey("users.user_id")),
      Column("shipped", Boolean(), default=False),
  )

  line_items = Table(
      "line_items",
      metadata,
      Column("line_items_id", Integer(), primary_key=True),
      Column("order_id", ForeignKey("orders.order_id")),
      Column("cookie_id", ForeignKey("cookies.cookie_id")),
      Column("quantity", Integer()),
      Column("extended_cost", Numeric(12, 2)),
  )

  from sqlalchemy.pool import StaticPool

  engine = create_engine("sqlite:///:memory:", echo=True)
  connection = engine.connect()
  metadata.create_all(connection)

Insert Data

  ins = cookies.insert().values(
      cookie_name="chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
      cookie_sku="CC01",
      quantity="12",
      unit_cost="0.50"
  )
  sqlf(ins)
INSERT INTO cookies (cookie_name, cookie_recipe_url, cookie_sku, quantity, unit_cost)
VALUES (:cookie_name, :cookie_recipe_url, :cookie_sku, :quantity, :unit_cost)
  import json

  json.dumps(ins.compile().params, indent=2)
{
  "cookie_name": "chocolate chip",
  "cookie_recipe_url": "http://some.aweso.me/cookie/recipe.html",
  "cookie_sku": "CC01",
  "quantity": "12",
  "unit_cost": "0.50"
}
  result = connection.execute(ins)
  connection.commit()
  result.inserted_primary_key
1
  ins = cookies.insert()
  result = connection.execute(
      ins,
      dict(
          cookie_name="dark chocolate chip",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe_dark.html",
          cookie_sku="CC02",
          quantity=1,
          unit_cost=0.75,
      ),
  )
  connection.commit()
  result.inserted_primary_key
2
  from sqlalchemy import insert
  ins = insert(cookies).values(
      cookie_name="ligth chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe_light.html",
      cookie_sku="CC03",
      quantity=10,
      unit_cost=0.5
  )
  result = connection.execute(ins)
  connection.commit()
  result.inserted_primary_key
3
  ins = cookies.insert()
  inventory_list = [
      {
          "cookie_name": "peanut butter",
          "cookie_recipe_url": "http://some.aweso.me/cookie/panut.html",
          "cookie_sku": "PB01",
          "quantity": 24,
          "unit_cost": 0.25
      },
      {
          "cookie_name": "oatmeal raisin",
          "cookie_recipe_url": "http://some.okay.me/cookie/raisin.html",
          "cookie_sku": "EWW01",
          "quantity": 100,
          "unit_cost": 1.00
      }
  ]

  result = connection.execute(ins, inventory_list)
  connection.commit()
  result.rowcount
2

Querying Data

  from sqlalchemy import select

  s = select(cookies)
  result = connection.execute(s)
  result.all()
1 chocolate chip http://some.aweso.me/cookie/recipe.html CC01 12 Decimal (0.50)
2 dark chocolate chip http://some.aweso.me/cookie/recipe_dark.html CC02 1 Decimal (0.75)
3 ligth chocolate chip http://some.aweso.me/cookie/recipe_light.html CC03 10 Decimal (0.50)
4 peanut butter http://some.aweso.me/cookie/panut.html PB01 24 Decimal (0.25)
5 oatmeal raisin http://some.okay.me/cookie/raisin.html EWW01 100 Decimal (1.00)
  s = select(cookies)
  results = connection.execute(s).all()
  results
1 chocolate chip http://some.aweso.me/cookie/recipe.html CC01 12 Decimal (0.50)
2 dark chocolate chip http://some.aweso.me/cookie/recipe_dark.html CC02 1 Decimal (0.75)
3 ligth chocolate chip http://some.aweso.me/cookie/recipe_light.html CC03 10 Decimal (0.50)
4 peanut butter http://some.aweso.me/cookie/panut.html PB01 24 Decimal (0.25)
5 oatmeal raisin http://some.okay.me/cookie/raisin.html EWW01 100 Decimal (1.00)
Result
  first_row = results[0]
  (
      first_row[1],
      first_row.cookie_name,
      first_row._mapping[cookies.c.cookie_name]
  )
chocolate chip chocolate chip chocolate chip
  rp = connection.execute(select(cookies))
  for record in rp:
      print(record.cookie_name)
2026-09-16 19:25:14,506 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_id, cookies.cookie_name, cookies.cookie_recipe_url, cookies.cookie_sku, cookies.quantity, cookies.unit_cost 
FROM cookies
2026-09-16 19:25:14,506 INFO sqlalchemy.engine.Engine [cached since 0.09276s ago] ()
chocolate chip
dark chocolate chip
ligth chocolate chip
peanut butter
oatmeal raisin
Controlling the Columns in the Query
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  rp = connection.execute(s)
  print(rp.keys())
  result = rp.first()
  print(result)
2026-09-16 19:25:14,520 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
2026-09-16 19:25:14,521 INFO sqlalchemy.engine.Engine [generated in 0.00015s] ()
RMKeyView(['cookie_name', 'quantity'])
('chocolate chip', 12)
Ordering
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(cookies.c.quantity)
  rp = connection.execute(s)
  for cookie in rp:
      print("{} - {}".format(cookie.quantity, cookie.cookie_name))
2026-09-16 19:25:14,532 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies ORDER BY cookies.quantity
2026-09-16 19:25:14,532 INFO sqlalchemy.engine.Engine [generated in 0.00017s] ()
1 - dark chocolate chip
10 - ligth chocolate chip
12 - chocolate chip
24 - peanut butter
100 - oatmeal raisin
  from sqlalchemy import desc
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(desc(cookies.c.quantity))
  rp = connection.execute(s)
  rp.all()
oatmeal raisin 100
peanut butter 24
chocolate chip 12
ligth chocolate chip 10
dark chocolate chip 1
Limiting
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  s = s.order_by(cookies.c.quantity)
  s = s.limit(2)
  rp = connection.execute(s)
  print([result.cookie_name for result in rp])
2026-09-16 19:25:14,582 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies ORDER BY cookies.quantity
 LIMIT ? OFFSET ?
2026-09-16 19:25:14,582 INFO sqlalchemy.engine.Engine [generated in 0.00020s] (2, 0)
['dark chocolate chip', 'ligth chocolate chip']
Functions and Labels
  from sqlalchemy import func

  s = select(func.sum(cookies.c.quantity))
  rp = connection.execute(s)
  print(rp.scalar())
2026-09-16 19:25:14,596 INFO sqlalchemy.engine.Engine SELECT sum(cookies.quantity) AS sum_1 
FROM cookies
2026-09-16 19:25:14,596 INFO sqlalchemy.engine.Engine [generated in 0.00015s] ()
147
  s = select(func.count(cookies.c.cookie_name))
  rp = connection.execute(s)
  record = rp.first()
  print(record._fields)
  print(record.count_1)
2026-09-16 19:25:14,609 INFO sqlalchemy.engine.Engine SELECT count(cookies.cookie_name) AS count_1 
FROM cookies
2026-09-16 19:25:14,610 INFO sqlalchemy.engine.Engine [generated in 0.00014s] ()
('count_1',)
5
  s = select(func.count(cookies.c.cookie_name).label('inventory_count'))
  rp = connection.execute(s)
  record = rp.first()
  print(record._fields)
  print(record.inventory_count)
2026-09-16 19:25:14,623 INFO sqlalchemy.engine.Engine SELECT count(cookies.cookie_name) AS inventory_count 
FROM cookies
2026-09-16 19:25:14,623 INFO sqlalchemy.engine.Engine [generated in 0.00015s] ()
('inventory_count',)
5
Filtering
  s = select(cookies).where(cookies.c.cookie_name == "chocolate chip")
  rp = connection.execute(s)
  record = rp.first()
  [
      (k, repr(v) if hasattr(v, "quantize") else v)
      for k, v in dict(record._mapping.items()).items()
  ]
cookie_id 1
cookie_name chocolate chip
cookie_recipe_url http://some.aweso.me/cookie/recipe.html
cookie_sku CC01
quantity 12
unit_cost Decimal('0.50')
  s = select(cookies).where(cookies.c.cookie_name.like("%chocolate%"))
  rp = connection.execute(s)
  for record in rp:
      print(record.cookie_name)
2026-09-16 19:25:14,672 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_id, cookies.cookie_name, cookies.cookie_recipe_url, cookies.cookie_sku, cookies.quantity, cookies.unit_cost 
FROM cookies 
WHERE cookies.cookie_name LIKE ?
2026-09-16 19:25:14,672 INFO sqlalchemy.engine.Engine [generated in 0.00017s] ('%chocolate%',)
chocolate chip
dark chocolate chip
ligth chocolate chip
  s = select(cookies.c.cookie_name, "SKU-" + cookies.c.cookie_sku)
  [row for row in connection.execute(s)]
chocolate chip SKU-CC01
dark chocolate chip SKU-CC02
ligth chocolate chip SKU-CC03
peanut butter SKU-PB01
oatmeal raisin SKU-EWW01
  from sqlalchemy import cast

  s = select(
      cookies.c.cookie_name,
      cast((cookies.c.quantity * cookies.c.unit_cost), Numeric(12, 2)).label("inv_cost"),
  )
  [(row.cookie_name, repr(row.inv_cost)) for row in connection.execute(s)]
chocolate chip Decimal('6.00')
dark chocolate chip Decimal('0.75')
ligth chocolate chip Decimal('5.00')
peanut butter Decimal('6.00')
oatmeal raisin Decimal('100.00')
Conjunctions
  from sqlalchemy import and_, or_

  s = select(cookies).where(and_(cookies.c.quantity > 23, cookies.c.unit_cost < 0.40))
  [row.cookie_name for row in connection.execute(s)]
peanut butter
  s = select(cookies).where(
      or_(cookies.c.quantity.between(10, 50), cookies.c.cookie_name.contains("chip"))
  )
  [(row.cookie_name,) for row in connection.execute(s)]
chocolate chip
dark chocolate chip
ligth chocolate chip
peanut butter

Updating Data

  from sqlalchemy import update

  u = update(cookies).where(cookies.c.cookie_name == "chocolate chip")
  u = u.values(quantity=(cookies.c.quantity + 120))
  result = connection.execute(u)
  connection.commit()
  result.rowcount
1
  s = select(cookies).where(cookies.c.cookie_name == "chocolate chip")
  result = connection.execute(s).first()
  [
      (
          key,
          (
              repr(result._mapping[key])
              if hasattr(result._mapping[key], "quantize")
              else result._mapping[key]
          ),
      )
      for key in result._mapping.keys()
  ]
cookie_id 1
cookie_name chocolate chip
cookie_recipe_url http://some.aweso.me/cookie/recipe.html
cookie_sku CC01
quantity 132
unit_cost Decimal('0.50')

Deleting Data

  from sqlalchemy import delete

  u = delete(cookies).where(cookies.c.cookie_name == "dark chocolate chip")
  result = connection.execute(u)
  connection.commit()
  result.rowcount
1
  s = select(cookies).where(cookies.c.cookie_name == "dark chocolate chip")
  result = connection.execute(s).all()
  len(result)
0

Joins

  customer_list = [
      {
          "username": "cookiemon",
          "email_address": "mon@cookie.com",
          "phone": "111-111-1111",
          "password": "password",
      },
      {
          "username": "cakeeater",
          "email_address": "cakeeater@cake.com",
          "phone": "222-222-2222",
          "password": "password",
      },
      {
          "username": "pieguy",
          "email_address": "guy@pie.com",
          "phone": "333-333-3333",
          "password": "password",
      },
  ]
  ins = users.insert()
  result = connection.execute(ins, customer_list)
  connection.commit()
  ins = insert(orders).values(user_id=1, order_id=1)
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {"order_id": 1, "cookie_id": 1, "quantity": 2, "extended_cost": 1.00},
      {"order_id": 1, "cookie_id": 3, "quantity": 12, "extended_cost": 3.00},
  ]
  result = connection.execute(ins, order_items)
  ins = insert(orders).values(user_id=2, order_id=2)
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {"order_id": 2, "cookie_id": 1, "quantity": 24, "extended_cost": 12.00},
      {"order_id": 2, "cookie_id": 4, "quantity": 6, "extended_cost": 6.00},
  ]
  result = connection.execute(ins, order_items)
  connection.commit()
  s = select(line_items)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
line_items_id order_id cookie_id quantity extended_cost
1 1 1 2 Decimal (1.00)
2 1 3 12 Decimal (3.00)
3 2 1 24 Decimal (12.00)
4 2 4 6 Decimal (6.00)
  s = select(orders)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
order_id user_id shipped
1 1 False
2 2 False
  s = select(users)
  rp = connection.execute(s)
  [tuple(f for f in rp.keys())] + [row for row in rp]
user_id username email_address phone password created_on updated_on
1 cookiemon mon@cookie.com 111-111-1111 password datetime.datetime (2026 9 16 19 25 14 928945) datetime.datetime (2026 9 16 19 25 14 928953)
2 cakeeater cakeeater@cake.com 222-222-2222 password datetime.datetime (2026 9 16 19 25 14 928955) datetime.datetime (2026 9 16 19 25 14 928956)
3 pieguy guy@pie.com 333-333-3333 password datetime.datetime (2026 9 16 19 25 14 928957) datetime.datetime (2026 9 16 19 25 14 928958)
  columns = [
      orders.c.order_id,
      users.c.username,
      users.c.phone,
      cookies.c.cookie_name,
      line_items.c.quantity,
      line_items.c.extended_cost,
  ]

  cookiemon_orders = (
      select(*columns)
      .select_from(
          orders
          .join(users, orders.c.user_id == users.c.user_id)
          .join(line_items, orders.c.order_id == line_items.c.order_id)
          .join(cookies, line_items.c.cookie_id == cookies.c.cookie_id)
      )
      .where(users.c.username == "cookiemon")
  )
  result = connection.execute(cookiemon_orders)
  [row for row in result]
1 cookiemon 111-111-1111 chocolate chip 2 Decimal (1.00)
1 cookiemon 111-111-1111 ligth chocolate chip 12 Decimal (3.00)

Chapter 3: Exceptions and Transactions

SQLAlchemy v1

  import sys

  sys.stderr = sys.stdout

  from datetime import datetime

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      Column,
      DateTime,
      ForeignKey,
      Integer,
      MetaData,
      Numeric,
      String,
      Table,
      create_engine,
      PrimaryKeyConstraint,
      UniqueConstraint,
  )
  from sqlparse import format as sqlformat


  def sqlf(
      sql: str | object,
      reindent=True,
      keyword_case="upper",
      indent_width=2,
      strip_comments=False,
      identifier_case="lower",
  ):
      if not isinstance(sql, str):
          sql = str(sql)
      return sqlformat(
          sql,
          encoding="utf8",
          reindent=reindent,
          keyword_case=keyword_case,
          indent_width=indent_width,
          strip_comments=strip_comments,
          identifier_case=identifier_case,
      )


  metadata = MetaData()

  cookies = Table(
      "cookies",
      metadata,
      Column("cookie_id", Integer(), primary_key=True),
      Column("cookie_name", String(50), index=True),
      Column("cookie_recipe_url", String(255), unique=True),
      Column("cookie_sku", String(55), unique=True),
      Column("quantity", Integer()),
      Column("unit_cost", Numeric(12, 2)),
      CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
      CheckConstraint("quantity >= 0", name="quantity_positive"),
  )

  users = Table(
      "users",
      metadata,
      Column("user_id", Integer()),
      Column("username", String(15), nullable=False),
      Column("email_address", String(255), nullable=False),
      Column("phone", String(20), nullable=False),
      Column("password", String(25), nullable=False),
      Column("created_on", DateTime(), default=datetime.now),
      Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      PrimaryKeyConstraint("user_id", name="user_pk"),
      UniqueConstraint("username", name="uix_username"),
  )

  orders = Table(
      "orders",
      metadata,
      Column("order_id", Integer(), primary_key=True),
      Column("user_id", ForeignKey("users.user_id")),
      Column("shipped", Boolean(), default=False),
  )


  line_items = Table(
      "line_items",
      metadata,
      Column("line_items_id", Integer(), primary_key=True),
      Column("order_id", ForeignKey("orders.order_id")),
      Column("cookie_id", ForeignKey("cookies.cookie_id")),
      Column("quantity", Integer()),
      Column("extended_cost", Numeric(12, 2)),
  )

  engine = create_engine("sqlite:///:memory:", echo=True)
  connection = engine.connect()
  metadata.create_all(engine)

AttributeError

  import logging
  from sqlalchemy import select, insert

  ins = insert(users).values(
      username="cookiemon",
      email_address="mon@cookie.com",
      phone="111-111-1111",
      password="password",
  )

  result = connection.execute(ins)

  s = select(users.c.username)
  results = connection.execute(s)

  try:
      [(result.username, result.password) for result in results]
  except AttributeError as exc:
      logging.exception(results)
2026-09-18 12:06:22,953 INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
2026-09-18 12:06:22,954 INFO sqlalchemy.engine.Engine INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
2026-09-18 12:06:22,954 INFO sqlalchemy.engine.Engine [generated in 0.00036s] ('cookiemon', 'mon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:06:22.953895', '2026-09-18 12:06:22.953904')
INFO:sqlalchemy.engine.Engine:[generated in 0.00036s] ('cookiemon', 'mon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:06:22.953895', '2026-09-18 12:06:22.953904')
2026-09-18 12:06:22,954 INFO sqlalchemy.engine.Engine SELECT users.username 
FROM users
INFO:sqlalchemy.engine.Engine:SELECT users.username 
FROM users
2026-09-18 12:06:22,954 INFO sqlalchemy.engine.Engine [generated in 0.00014s] ()
INFO:sqlalchemy.engine.Engine:[generated in 0.00014s] ()
ERROR:root:<sqlalchemy.engine.cursor.CursorResult object at 0x7fa8f6747950>
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 211, in _key_not_found
    self._key_fallback(key, None)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/cursor.py", line 851, in _key_fallback
    raise exc.NoSuchColumnError(
    ...<2 lines>...
    ) from err
sqlalchemy.exc.NoSuchColumnError: Could not locate column in row for column 'password'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-VQah8h", line 17, in <module>
    [(result.username, result.password) for result in results]
                       ^^^^^^^^^^^^^^^
  File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
  File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 213, in _key_not_found
    raise AttributeError(ke.args[0]) from ke
AttributeError: Could not locate column in row for column 'password'

IntegrityError

  from sqlalchemy.exc import IntegrityError

  s = select(users.c.username)
  print(connection.execute(s).fetchall())

  ins = insert(users).values(
      username="cookiemon",
      email_address="damon@cookie.com",
      phone="111-111-1111",
      password="password",
  )
  try:
      result = connection.execute(ins)
  except IntegrityError as exc:
      logging.exception(str(ins))
2026-09-18 12:06:22,969 INFO sqlalchemy.engine.Engine SELECT users.username 
FROM users
INFO:sqlalchemy.engine.Engine:SELECT users.username 
FROM users
2026-09-18 12:06:22,969 INFO sqlalchemy.engine.Engine [cached since 0.01439s ago] ()
INFO:sqlalchemy.engine.Engine:[cached since 0.01439s ago] ()
[('cookiemon',)]
2026-09-18 12:06:22,969 INFO sqlalchemy.engine.Engine INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
2026-09-18 12:06:22,969 INFO sqlalchemy.engine.Engine [cached since 0.0159s ago] ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:06:22.969623', '2026-09-18 12:06:22.969627')
INFO:sqlalchemy.engine.Engine:[cached since 0.0159s ago] ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:06:22.969623', '2026-09-18 12:06:22.969627')
ERROR:root:INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (:username, :email_address, :phone, :password, :created_on, :updated_on)
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: UNIQUE constraint failed: users.username

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-Dqwfvm", line 13, in <module>
    result = connection.execute(ins)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: users.username
[SQL: INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)]
[parameters: ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:06:22.969623', '2026-09-18 12:06:22.969627')]
(Background on this error at: https://sqlalche.me/e/20/gkpj)

Transactions

  ins = cookies.insert()
  inventory_list = [
      {
          "cookie_name": "chocolate chip",
          "cookie_recipe_url": "http://some.aweso.me/cookie/recipe.html",
          "cookie_sku": "CC01",
          "quantity": "12",
          "unit_cost": "0.5",
      },
      {
          "cookie_name": "dark chocolate chip",
          "cookie_recipe_url": "http://some.aweso.me/cookie/recipe_dark.html",
          "cookie_sku": "CC02",
          "quantity": "1",
          "unit_cost": "0.75",
      },
  ]
  result = connection.execute(ins, inventory_list)
  ins = insert(orders).values(user_id=1, order_id="1")
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {
          "order_id": 1,
          "cookie_id": 1,
          "quantity": 9,
          "extended_cost": 4.50,
      }
  ]
  result = connection.execute(ins, order_items)

  ins = insert(orders).values(user_id=1, order_id="2")
  result = connection.execute(ins)
  ins = insert(line_items)
  order_items = [
      {
          "order_id": 2,
          "cookie_id": 1,
          "quantity": 4,
          "extended_cost": 1.50,
      },
      {
          "order_id": 2,
          "cookie_id": 2,
          "quantity": 1,
          "extended_cost": 4.50,
      },
  ]
  result = connection.execute(ins, order_items)
  from sqlalchemy import update


  def ship_it(order_id: int) -> None:
      s = select(line_items.c.cookie_id, line_items.c.quantity)
      s = s.where(line_items.c.order_id == order_id)
      cookies_to_ship = connection.execute(s)
      for cookie in cookies_to_ship:
          u = (
              update(cookies)
              .where(cookies.c.cookie_id == cookie.cookie_id)
              .values(quantity=cookies.c.quantity - cookie.quantity)
          )
          result = connection.execute(u)
      u = update(orders).where(orders.c.order_id == order_id).values(shipped=True)
      connection.execute(u)
      print(f"Shipped order ID: {order_id}")
  ship_it(1)
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  print(connection.execute(s).fetchall())
2026-09-18 12:06:23,066 INFO sqlalchemy.engine.Engine SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
INFO:sqlalchemy.engine.Engine:SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
2026-09-18 12:06:23,066 INFO sqlalchemy.engine.Engine [generated in 0.00024s] (1,)
INFO:sqlalchemy.engine.Engine:[generated in 0.00024s] (1,)
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine [generated in 0.00009s] (9, 1)
INFO:sqlalchemy.engine.Engine:[generated in 0.00009s] (9, 1)
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine UPDATE orders SET shipped=? WHERE orders.order_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE orders SET shipped=? WHERE orders.order_id = ?
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine [generated in 0.00008s] (1, 1)
INFO:sqlalchemy.engine.Engine:[generated in 0.00008s] (1, 1)
Shipped order ID: 1
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
INFO:sqlalchemy.engine.Engine:SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
2026-09-18 12:06:23,067 INFO sqlalchemy.engine.Engine [generated in 0.00006s] ()
INFO:sqlalchemy.engine.Engine:[generated in 0.00006s] ()
[('chocolate chip', 3), ('dark chocolate chip', 1)]
  try:
      ship_it(2)
  except IntegrityError:
      logging.exception("")
2026-09-18 12:07:29,522 INFO sqlalchemy.engine.Engine SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
INFO:sqlalchemy.engine.Engine:SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
2026-09-18 12:07:29,522 INFO sqlalchemy.engine.Engine [cached since 66.46s ago] (2,)
INFO:sqlalchemy.engine.Engine:[cached since 66.46s ago] (2,)
2026-09-18 12:07:29,523 INFO sqlalchemy.engine.Engine UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
2026-09-18 12:07:29,523 INFO sqlalchemy.engine.Engine [cached since 66.46s ago] (4, 1)
INFO:sqlalchemy.engine.Engine:[cached since 66.46s ago] (4, 1)
ERROR:root:
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: CHECK constraint failed: quantity_positive

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-swDwMp", line 2, in <module>
    ship_it(2)
    ~~~~~~~^^^
  File "<string>", line 14, in ship_it
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
[SQL: UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?]
[parameters: (4, 1)]
(Background on this error at: https://sqlalche.me/e/20/gkpj)
  def ship_it(order_id: int) -> None:
      s = select(line_items.c.cookie_id, line_items.c.quantity)
      s = s.where(line_items.c.order_id == order_id)
      transaction = connection.begin()
      cookies_to_ship = connection.execute(s).fetchall()

      try:
          for cookie in cookies_to_ship:
              u = update(cookies).where(cookies.c.cookie_id == cookie.cookie_id).values(quantity = cookies.c.quantity -cookie.quantity)
              result = connection.execute(u)
          u = update(orders).where(orders.c.order_id == order_id).values(shipped=True)
          result = connection.execute(u)
          print(f"Shipped order ID: {order_id}")
          transaction.commit()
      except IntegrityError as error:
          transaction.rollback()
          logging.exception()

SQLAlchemy v2

  #!/usr/bin/env python3
  import sys

  sys.stderr = sys.stdout

  import logging
  from datetime import datetime

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      Column,
      DateTime,
      ForeignKey,
      Integer,
      MetaData,
      Numeric,
      PrimaryKeyConstraint,
      String,
      Table,
      UniqueConstraint,
      create_engine,
      insert,
      select,
      update,
  )
  from sqlalchemy.exc import IntegrityError
  from sqlparse import format as sqlformat


  def sqlf(
      sql: str | object,
      reindent=True,
      keyword_case="upper",
      indent_width=2,
      strip_comments=False,
      identifier_case="lower",
  ):
      if not isinstance(sql, str):
          sql = str(sql)
      return sqlformat(
          sql,
          encoding="utf8",
          reindent=reindent,
          keyword_case=keyword_case,
          indent_width=indent_width,
          strip_comments=strip_comments,
          identifier_case=identifier_case,
      )


  metadata = MetaData()

  cookies = Table(
      "cookies",
      metadata,
      Column("cookie_id", Integer(), primary_key=True),
      Column("cookie_name", String(50), index=True),
      Column("cookie_recipe_url", String(255), unique=True),
      Column("cookie_sku", String(55), unique=True),
      Column("quantity", Integer()),
      Column("unit_cost", Numeric(12, 2)),
      CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
      CheckConstraint("quantity >= 0", name="quantity_positive"),
  )

  users = Table(
      "users",
      metadata,
      Column("user_id", Integer()),
      Column("username", String(15), nullable=False),
      Column("email_address", String(255), nullable=False),
      Column("phone", String(20), nullable=False),
      Column("password", String(25), nullable=False),
      Column("created_on", DateTime(), default=datetime.now),
      Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      PrimaryKeyConstraint("user_id", name="user_pk"),
      UniqueConstraint("username", name="uix_username"),
  )

  orders = Table(
      "orders",
      metadata,
      Column("order_id", Integer(), primary_key=True),
      Column("user_id", ForeignKey("users.user_id")),
      Column("shipped", Boolean(), default=False),
  )

  line_items = Table(
      "line_items",
      metadata,
      Column("line_items_id", Integer(), primary_key=True),
      Column("order_id", ForeignKey("orders.order_id")),
      Column("cookie_id", ForeignKey("cookies.cookie_id")),
      Column("quantity", Integer()),
      Column("extended_cost", Numeric(12, 2)),
  )

  engine = create_engine("sqlite:///:memory:", echo=True)

  with engine.begin() as connection:
      metadata.create_all(connection)

      # --- insert одного пользователя ---
      ins = insert(users).values(
          username="cookiemon",
          email_address="mon@cookie.com",
          phone="111-111-1111",
          password="password",
      )
      connection.execute(ins)

      # --- select возвращает Row, у которого есть .username ---
      s = select(users.c.username, users.c.password)
      results = connection.execute(s)
      try:
          [(row.username, row.password) for row in results]
      except AttributeError:
          logging.exception(results)

      # --- проверка вставки ---
      s = select(users.c.username)
      print(connection.execute(s).fetchall())

      # --- нарушение уникальности username ---
      ins = insert(users).values(
          username="cookiemon",
          email_address="damon@cookie.com",
          phone="111-111-1111",
          password="password",
      )
      try:
          with connection.begin_nested():  # SAVEPOINT, чтобы не убить внешнюю транзакцию
              connection.execute(ins)
      except IntegrityError:
          logging.exception(str(ins))

      # --- массовая вставка cookies ---
      inventory_list = [
          {
              "cookie_name": "chocolate chip",
              "cookie_recipe_url": "http://some.aweso.me/cookie/recipe.html",
              "cookie_sku": "CC01",
              "quantity": "12",
              "unit_cost": "0.5",
          },
          {
              "cookie_name": "dark chocolate chip",
              "cookie_recipe_url": "http://some.aweso.me/cookie/recipe_dark.html",
              "cookie_sku": "CC02",
              "quantity": "1",
              "unit_cost": "0.75",
          },
      ]
      connection.execute(insert(cookies), inventory_list)

      # --- заказы ---
      connection.execute(insert(orders).values(user_id=1, order_id="1"))
      connection.execute(
          insert(line_items),
          [
              {
                  "order_id": 1,
                  "cookie_id": 1,
                  "quantity": 9,
                  "extended_cost": 4.50,
              }
          ],
      )

      connection.execute(insert(orders).values(user_id=1, order_id="2"))
      connection.execute(
          insert(line_items),
          [
              {
                  "order_id": 2,
                  "cookie_id": 1,
                  "quantity": 4,
                  "extended_cost": 1.50,
              },
              {
                  "order_id": 2,
                  "cookie_id": 2,
                  "quantity": 1,
                  "extended_cost": 4.50,
              },
          ],
      )


  def ship_it(order_id: int) -> None:
      """Версия без явной транзакции — каждый execute коммитится сам."""
      s = select(line_items.c.cookie_id, line_items.c.quantity).where(
          line_items.c.order_id == order_id
      )
      cookies_to_ship = connection.execute(s).fetchall()
      for cookie in cookies_to_ship:
          u = (
              update(cookies)
              .where(cookies.c.cookie_id == cookie.cookie_id)
              .values(quantity=cookies.c.quantity - cookie.quantity)
          )
          connection.execute(u)
      u = update(orders).where(orders.c.order_id == order_id).values(shipped=True)
      connection.execute(u)
      print(f"Shipped order ID: {order_id}")


  def ship_it_atomic(order_id: int) -> None:
      """Версия с явной транзакцией и rollback при ошибке."""
      s = select(line_items.c.cookie_id, line_items.c.quantity).where(
          line_items.c.order_id == order_id
      )

      with engine.begin() as conn:
          cookies_to_ship = conn.execute(s).fetchall()
          try:
              for cookie in cookies_to_ship:
                  u = (
                      update(cookies)
                      .where(cookies.c.cookie_id == cookie.cookie_id)
                      .values(quantity=cookies.c.quantity - cookie.quantity)
                  )
                  conn.execute(u)
              u = update(orders).where(orders.c.order_id == order_id).values(shipped=True)
              conn.execute(u)
              print(f"Shipped order ID: {order_id}")
          except IntegrityError:
              # with engine.begin() сам сделает rollback
              logging.exception("Failed to ship order %s", order_id)
              raise


  # --- используем транзакционную версию ---
  ship_it_atomic(1)
  s = select(cookies.c.cookie_name, cookies.c.quantity)
  with engine.connect() as conn:
      print(conn.execute(s).fetchall())

  try:
      ship_it_atomic(2)
  except IntegrityError:
      logging.exception("")
2026-09-18 12:21:58,909 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("cookies")
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("cookies")
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("users")
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("users")
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,910 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("orders")
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("orders")
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("line_items")
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("line_items")
2026-09-18 12:21:58,911 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-18 12:21:58,912 INFO sqlalchemy.engine.Engine 
CREATE TABLE cookies (
	cookie_id INTEGER NOT NULL, 
	cookie_name VARCHAR(50), 
	cookie_recipe_url VARCHAR(255), 
	cookie_sku VARCHAR(55), 
	quantity INTEGER, 
	unit_cost NUMERIC(12, 2), 
	PRIMARY KEY (cookie_id), 
	CONSTRAINT unit_cost_positive CHECK (unit_cost >= 0.00), 
	CONSTRAINT quantity_positive CHECK (quantity >= 0), 
	UNIQUE (cookie_recipe_url), 
	UNIQUE (cookie_sku)
)


2026-09-18 12:21:58,912 INFO sqlalchemy.engine.Engine [no key 0.00007s] ()
2026-09-18 12:21:58,912 INFO sqlalchemy.engine.Engine CREATE INDEX ix_cookies_cookie_name ON cookies (cookie_name)
2026-09-18 12:21:58,912 INFO sqlalchemy.engine.Engine [no key 0.00006s] ()
2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine 
CREATE TABLE users (
	user_id INTEGER NOT NULL, 
	username VARCHAR(15) NOT NULL, 
	email_address VARCHAR(255) NOT NULL, 
	phone VARCHAR(20) NOT NULL, 
	password VARCHAR(25) NOT NULL, 
	created_on DATETIME, 
	updated_on DATETIME, 
	CONSTRAINT user_pk PRIMARY KEY (user_id), 
	CONSTRAINT uix_username UNIQUE (username)
)


2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine [no key 0.00006s] ()
2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine 
CREATE TABLE orders (
	order_id INTEGER NOT NULL, 
	user_id INTEGER, 
	shipped BOOLEAN, 
	PRIMARY KEY (order_id), 
	FOREIGN KEY(user_id) REFERENCES users (user_id)
)


2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine [no key 0.00005s] ()
2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine 
CREATE TABLE line_items (
	line_items_id INTEGER NOT NULL, 
	order_id INTEGER, 
	cookie_id INTEGER, 
	quantity INTEGER, 
	extended_cost NUMERIC(12, 2), 
	PRIMARY KEY (line_items_id), 
	FOREIGN KEY(order_id) REFERENCES orders (order_id), 
	FOREIGN KEY(cookie_id) REFERENCES cookies (cookie_id)
)


2026-09-18 12:21:58,913 INFO sqlalchemy.engine.Engine [no key 0.00005s] ()
2026-09-18 12:21:58,915 INFO sqlalchemy.engine.Engine INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
2026-09-18 12:21:58,915 INFO sqlalchemy.engine.Engine [generated in 0.00019s] ('cookiemon', 'mon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:21:58.915122', '2026-09-18 12:21:58.915127')
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine SELECT users.username, users.password 
FROM users
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine [generated in 0.00008s] ()
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine SELECT users.username 
FROM users
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine [generated in 0.00007s] ()
[('cookiemon',)]
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_1
2026-09-18 12:21:58,916 INFO sqlalchemy.engine.Engine [no key 0.00006s] ()
2026-09-18 12:21:58,917 INFO sqlalchemy.engine.Engine INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)
2026-09-18 12:21:58,917 INFO sqlalchemy.engine.Engine [cached since 0.002053s ago] ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:21:58.917037', '2026-09-18 12:21:58.917039')
2026-09-18 12:21:58,917 INFO sqlalchemy.engine.Engine ROLLBACK TO SAVEPOINT sa_savepoint_1
2026-09-18 12:21:58,917 INFO sqlalchemy.engine.Engine [no key 0.00006s] ()
ERROR:root:INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (:username, :email_address, :phone, :password, :created_on, :updated_on)
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: UNIQUE constraint failed: users.username

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-ObJFq6", line 134, in <module>
    connection.execute(ins)
    ~~~~~~~~~~~~~~~~~~^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: users.username
[SQL: INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)]
[parameters: ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2026-09-18 12:21:58.917037', '2026-09-18 12:21:58.917039')]
(Background on this error at: https://sqlalche.me/e/20/gkpj)
2026-09-18 12:21:58,921 INFO sqlalchemy.engine.Engine INSERT INTO cookies (cookie_name, cookie_recipe_url, cookie_sku, quantity, unit_cost) VALUES (?, ?, ?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO cookies (cookie_name, cookie_recipe_url, cookie_sku, quantity, unit_cost) VALUES (?, ?, ?, ?, ?)
2026-09-18 12:21:58,921 INFO sqlalchemy.engine.Engine [generated in 0.00022s] [('chocolate chip', 'http://some.aweso.me/cookie/recipe.html', 'CC01', '12', 0.5), ('dark chocolate chip', 'http://some.aweso.me/cookie/recipe_dark.html', 'CC02', '1', 0.75)]
INFO:sqlalchemy.engine.Engine:[generated in 0.00022s] [('chocolate chip', 'http://some.aweso.me/cookie/recipe.html', 'CC01', '12', 0.5), ('dark chocolate chip', 'http://some.aweso.me/cookie/recipe_dark.html', 'CC02', '1', 0.75)]
2026-09-18 12:21:58,922 INFO sqlalchemy.engine.Engine INSERT INTO orders (order_id, user_id, shipped) VALUES (?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO orders (order_id, user_id, shipped) VALUES (?, ?, ?)
2026-09-18 12:21:58,922 INFO sqlalchemy.engine.Engine [generated in 0.00015s] ('1', 1, 0)
INFO:sqlalchemy.engine.Engine:[generated in 0.00015s] ('1', 1, 0)
2026-09-18 12:21:58,922 INFO sqlalchemy.engine.Engine INSERT INTO line_items (order_id, cookie_id, quantity, extended_cost) VALUES (?, ?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO line_items (order_id, cookie_id, quantity, extended_cost) VALUES (?, ?, ?, ?)
2026-09-18 12:21:58,922 INFO sqlalchemy.engine.Engine [generated in 0.00014s] (1, 1, 9, 4.5)
INFO:sqlalchemy.engine.Engine:[generated in 0.00014s] (1, 1, 9, 4.5)
2026-09-18 12:21:58,923 INFO sqlalchemy.engine.Engine INSERT INTO orders (order_id, user_id, shipped) VALUES (?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO orders (order_id, user_id, shipped) VALUES (?, ?, ?)
2026-09-18 12:21:58,923 INFO sqlalchemy.engine.Engine [cached since 0.001143s ago] ('2', 1, 0)
INFO:sqlalchemy.engine.Engine:[cached since 0.001143s ago] ('2', 1, 0)
2026-09-18 12:21:58,923 INFO sqlalchemy.engine.Engine INSERT INTO line_items (order_id, cookie_id, quantity, extended_cost) VALUES (?, ?, ?, ?)
INFO:sqlalchemy.engine.Engine:INSERT INTO line_items (order_id, cookie_id, quantity, extended_cost) VALUES (?, ?, ?, ?)
2026-09-18 12:21:58,923 INFO sqlalchemy.engine.Engine [generated in 0.00011s] [(2, 1, 4, 1.5), (2, 2, 1, 4.5)]
INFO:sqlalchemy.engine.Engine:[generated in 0.00011s] [(2, 1, 4, 1.5), (2, 2, 1, 4.5)]
2026-09-18 12:21:58,923 INFO sqlalchemy.engine.Engine COMMIT
INFO:sqlalchemy.engine.Engine:COMMIT
2026-09-18 12:21:58,924 INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
2026-09-18 12:21:58,924 INFO sqlalchemy.engine.Engine SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
INFO:sqlalchemy.engine.Engine:SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
2026-09-18 12:21:58,924 INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1,)
INFO:sqlalchemy.engine.Engine:[generated in 0.00010s] (1,)
2026-09-18 12:21:58,926 INFO sqlalchemy.engine.Engine UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
2026-09-18 12:21:58,926 INFO sqlalchemy.engine.Engine [generated in 0.00009s] (9, 1)
INFO:sqlalchemy.engine.Engine:[generated in 0.00009s] (9, 1)
2026-09-18 12:21:58,926 INFO sqlalchemy.engine.Engine UPDATE orders SET shipped=? WHERE orders.order_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE orders SET shipped=? WHERE orders.order_id = ?
2026-09-18 12:21:58,926 INFO sqlalchemy.engine.Engine [generated in 0.00012s] (1, 1)
INFO:sqlalchemy.engine.Engine:[generated in 0.00012s] (1, 1)
Shipped order ID: 1
2026-09-18 12:21:58,926 INFO sqlalchemy.engine.Engine COMMIT
INFO:sqlalchemy.engine.Engine:COMMIT
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
INFO:sqlalchemy.engine.Engine:SELECT cookies.cookie_name, cookies.quantity 
FROM cookies
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine [generated in 0.00012s] ()
INFO:sqlalchemy.engine.Engine:[generated in 0.00012s] ()
[('chocolate chip', 3), ('dark chocolate chip', 1)]
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine ROLLBACK
INFO:sqlalchemy.engine.Engine:ROLLBACK
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
INFO:sqlalchemy.engine.Engine:SELECT line_items.cookie_id, line_items.quantity 
FROM line_items 
WHERE line_items.order_id = ?
2026-09-18 12:21:58,927 INFO sqlalchemy.engine.Engine [cached since 0.002892s ago] (2,)
INFO:sqlalchemy.engine.Engine:[cached since 0.002892s ago] (2,)
2026-09-18 12:21:58,928 INFO sqlalchemy.engine.Engine UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
INFO:sqlalchemy.engine.Engine:UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?
2026-09-18 12:21:58,928 INFO sqlalchemy.engine.Engine [cached since 0.002047s ago] (4, 1)
INFO:sqlalchemy.engine.Engine:[cached since 0.002047s ago] (4, 1)
ERROR:root:Failed to ship order 2
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: CHECK constraint failed: quantity_positive

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-ObJFq6", line 224, in ship_it_atomic
    conn.execute(u)
    ~~~~~~~~~~~~^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
[SQL: UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?]
[parameters: (4, 1)]
(Background on this error at: https://sqlalche.me/e/20/gkpj)
2026-09-18 12:21:58,930 INFO sqlalchemy.engine.Engine ROLLBACK
INFO:sqlalchemy.engine.Engine:ROLLBACK
ERROR:root:
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: CHECK constraint failed: quantity_positive

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-sg4Ps9/python-ObJFq6", line 241, in <module>
    ship_it_atomic(2)
    ~~~~~~~~~~~~~~^^^
  File "/tmp/babel-sg4Ps9/python-ObJFq6", line 224, in ship_it_atomic
    conn.execute(u)
    ~~~~~~~~~~~~^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
[SQL: UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?]
[parameters: (4, 1)]
(Background on this error at: https://sqlalche.me/e/20/gkpj)

Chapter 4: Testing

  from datetime import datetime
  from sqlalchemy import (
      MetaData,
      Table,
      Column,
      Integer,
      Numeric,
      String,
      DateTime,
      ForeignKey,
      Boolean,
      create_engine,
  )
  from sqlalchemy.sql import insert


  class DataAccessLayer:
      connection = None
      engine = None
      conn_string = None
      metadata = MetaData()
      cookies = Table(
          "cookies",
          metadata,
          Column("cookie_id", Integer(), primary_key=True),
          Column("cookie_name", String(50), index=True),
          Column("cookie_recipe_url", String(255)),
          Column("cookie_sku", String(55)),
          Column("quantity", Integer()),
          Column("unit_cost", Numeric(12, 2)),
      )

      users = Table(
          "users",
          metadata,
          Column("user_id", Integer(), primary_key=True),
          Column("customer_number", Integer(), autoincrement=True),
          Column("username", String(15), nullable=False, unique=True),
          Column("email_address", String(255), nullable=False),
          Column("phone", String(20), nullable=False),
          Column("password", String(25), nullable=False),
          Column("created_on", DateTime(), default=datetime.now),
          Column("updated_on", DateTime(), default=datetime.now, onupdate=datetime.now),
      )

      orders = Table(
          "orders",
          metadata,
          Column("order_id", Integer()),
          Column("user_id", ForeignKey("users.user_id")),
          Column("shipped", Boolean(), default=False),
      )

      line_items = Table(
          "line_items",
          metadata,
          Column("line_items_id", Integer(), primary_key=True),
          Column("order_id", ForeignKey("orders.order_id")),
          Column("cookie_id", ForeignKey("cookies.cookie_id")),
          Column("quantity", Integer()),
          Column("extended_cost", Numeric(12, 2)),
      )

      def db_init(self, conn_string):
          self.engine = create_engine(conn_string or self.conn_string)
          self.metadata.create_all(self.engine)
          self.connection = self.engine.connect()


  dal = DataAccessLayer()


  def prep_db():
      ins = dal.cookies.insert()
      dal.connection.execute(
          ins,
          dict(
              cookie_name="dark chocolate chip",
              cookie_recipe_url="http://some.aweso.me/cookie/recipe_dark.html",
              cookie_sku="CC02",
              quantity="1",
              unit_cost="0.75",
          ),
      )
      inventory_list = [
          {
              "cookie_name": "peanut butter",
              "cookie_recipe_url": "http://some.aweso.me/cookie/peanut.html",
              "cookie_sku": "PB01",
              "quantity": "24",
              "unit_cost": "0.25",
          },
          {
              "cookie_name": "oatmeal raisin",
              "cookie_recipe_url": "http://some.okay.me/cookie/raisin.html",
              "cookie_sku": "EWW01",
              "quantity": "100",
              "unit_cost": "1.00",
          },
      ]
      dal.connection.execute(ins, inventory_list)

      customer_list = [
          {
              "username": "cookiemon",
              "email_address": "mon@cookie.com",
              "phone": "111-111-1111",
              "password": "password",
          },
          {
              "username": "cakeeater",
              "email_address": "cakeeater@cake.com",
              "phone": "222-222-2222",
              "password": "password",
          },
          {
              "username": "pieguy",
              "email_address": "guy@pie.com",
              "phone": "333-333-3333",
              "password": "password",
          },
      ]
      ins = dal.users.insert()
      dal.connection.execute(ins, customer_list)
      ins = insert(dal.orders).values(user_id=1, order_id="wlk001")
      dal.connection.execute(ins)
      ins = insert(dal.line_items)
      order_items = [
          {"order_id": "wlk001", "cookie_id": 1, "quantity": 2, "extended_cost": 1.00},
          {"order_id": "wlk001", "cookie_id": 3, "quantity": 12, "extended_cost": 3.00},
      ]
      dal.connection.execute(ins, order_items)
      ins = insert(dal.orders).values(user_id=2, order_id="ol001")
      dal.connection.execute(ins)
      ins = insert(dal.line_items)
      order_items = [
          {"order_id": "ol001", "cookie_id": 1, "quantity": 24, "extended_cost": 12.00},
          {"order_id": "ol001", "cookie_id": 4, "quantity": 6, "extended_cost": 6.00},
      ]
      dal.connection.execute(ins, order_items)
  from db import dal
  from sqlalchemy.sql import select


  def get_orders_by_customer(
      cust_name: str, shipped: bool | None = None, details: bool = False
  ):
      columns = [dal.orders.c.order_id, dal.users.c.username, dal.users.c.phone]
      joins = dal.users.join(dal.orders)
      if details:
          columns.extend(
              [
                  dal.cookies.c.cookie_name,
                  dal.line_items.c.quantity,
                  dal.line_items.c.extended_cost,
              ]
          )
          joins = joins.join(dal.line_items).join(dal.cookies)
      cust_orders = select(*columns)
      cust_orders = cust_orders.select_from(joins).where(
          dal.users.c.username == cust_name
      )
      if shipped is not None:
          cust_orders = cust_orders.where(dal.orders.c.shipped == shipped)
      return dal.connection.execute(cust_orders).fetchall()

UnitTest

  import unittest
  from decimal import Decimal

  from app import get_orders_by_customer
  from db import dal, prep_db


  class TestApp(unittest.TestCase):
      cookie_orders = [("wlk001", "cookiemon", "111-111-1111")]
      cookie_details = [
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "dark chocolate chip",
              2,
              Decimal("1.00"),
          ),
          ("wlk001", "cookiemon", "111-111-1111", "oatmeal raisin", 12, Decimal("3.00")),
      ]

      @classmethod
      def setUpClass(cls):
          dal.db_init("sqlite:///:memory:")
          prep_db()

      def test_orders_by_customer_blank(self):
          """
          - значение cust_name пустая строка,
          - shipped = None
          """
          results = get_orders_by_customer("")
          self.assertEqual(results, [])

      def test_orders_by_customer_blank_shipped(self):
          """
          - значение cust_name пустая строка,
          - shipped = True
          """
          results = get_orders_by_customer("", True)
          self.assertEqual(results, [])

      def test_orders_by_customer_blank_notshipped(self):
          """
          - значение cust_name пустая строка,
          - shipped = False
          """
          results = get_orders_by_customer("", False)
          self.assertEqual(results, [])

      def test_orders_by_customer_blank_details(self):
          results = get_orders_by_customer("", details=True)
          self.assertEqual(results, [])

      def test_orders_by_customer_blank_shipped_details(self):
          results = get_orders_by_customer("", True, True)
          self.assertEqual(results, [])

      def test_orders_by_customer_blank_notshipped_details(self):
          results = get_orders_by_customer("", False, True)
          self.assertEqual(results, [])

      def test_orders_by_customer(self):
          expected_results = [("wlk001", "cookiemon", "111-111-1111")]
          results = get_orders_by_customer("cookiemon")
          self.assertEqual(results, expected_results)

      def test_orders_by_customer_shipped_only(self):
          results = get_orders_by_customer("cookiemon", True)
          self.assertEqual(results, [])

      def test_orders_by_customer_unshipped_only(self):
          expected_results = [("wlk001", "cookiemon", "111-111-1111")]
          results = get_orders_by_customer("cookiemon", False)
          self.assertEqual(results, expected_results)

      def test_orders_by_customer_with_details(self):
          expected_results = [
              (
                  "wlk001",
                  "cookiemon",
                  "111-111-1111",
                  "dark chocolate chip",
                  2,
                  Decimal("1.00"),
              ),
              (
                  "wlk001",
                  "cookiemon",
                  "111-111-1111",
                  "oatmeal raisin",
                  12,
                  Decimal("3.00"),
              ),
          ]
          results = get_orders_by_customer("cookiemon", details=True)
          self.assertEqual(results, expected_results)

      def test_orders_by_customer_shipped_only_with_details(self):
          results = get_orders_by_customer("cookiemon", True, True)
          self.assertEqual(results, [])

      def test_orders_by_customer_unshipped_only_details(self):
          expected_results = [
              (
                  "wlk001",
                  "cookiemon",
                  "111-111-1111",
                  "dark chocolate chip",
                  2,
                  Decimal("1.00"),
              ),
              (
                  "wlk001",
                  "cookiemon",
                  "111-111-1111",
                  "oatmeal raisin",
                  12,
                  Decimal("3.00"),
              ),
          ]
          results = get_orders_by_customer("cookiemon", False, True)
          self.assertEqual(results, expected_results)
  import unittest
  from decimal import Decimal
  from unittest import mock

  from app import get_orders_by_customer
  from db import dal, prep_db


  class TestApp(unittest.TestCase):
      cookie_orders = [("wlk001", "cookiemon", "111-111-1111")]
      cookie_details = [
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "dark chocolate chip",
              2,
              Decimal("1.00"),
          ),
          ("wlk001", "cookiemon", "111-111-1111", "oatmeal raisin", 12, Decimal("3.00")),
      ]

      @mock.patch("app.select")
      @mock.patch("app.dal.connection")
      def test_orders_by_customer_blank(self, mock_conn, mock_select):
          mock_select.return_value.select_from.return_value.where.return_value = None
          mock_conn.execute.return_value.fetchall.return_value = []
          results = get_orders_by_customer("")
          self.assertEqual(results, [])

      @mock.patch("app.dal.connection")
      def test_orders_by_customer_blank_shipped(self, mock_conn):
          mock_conn.execute.return_value.fetchall.return_value = []
          results = get_orders_by_customer("", True)
          self.assertEqual(results, [])

      @mock.patch("app.dal.connection")
      def test_orders_by_customer(self, mock_conn):
          mock_conn.execute.return_value.fetchall.return_value = self.cookie_orders
          results = get_orders_by_customer("cookiemon")
          self.assertEqual(results, self.cookie_orders)

pytest

  from decimal import Decimal

  import pytest

  from app import get_orders_by_customer
  from db import dal, prep_db


  @pytest.fixture(autouse=True, scope="class")
  def setup_db():
      dal.db_init("sqlite:///:memory:")
      prep_db()
      yield
      # здесь можно закрыть соединение, если нужно:
      # dal.connection.close()


  def test_orders_by_customer_blank():
      """
      - значение cust_name пустая строка,
      - shipped = None
      """
      results = get_orders_by_customer("")
      assert results == []


  def test_orders_by_customer_blank_shipped():
      """
      - значение cust_name пустая строка,
      - shipped = True
      """
      results = get_orders_by_customer("", True)
      assert results == []


  def test_orders_by_customer_blank_notshipped():
      """
      - значение cust_name пустая строка,
      - shipped = False
      """
      results = get_orders_by_customer("", False)
      assert results == []


  def test_orders_by_customer_blank_details():
      results = get_orders_by_customer("", details=True)
      assert results == []


  def test_orders_by_customer_blank_shipped_details():
      results = get_orders_by_customer("", True, True)
      assert results == []


  def test_orders_by_customer_blank_notshipped_details():
      results = get_orders_by_customer("", False, True)
      assert results == []


  def test_orders_by_customer():
      expected_results = [("wlk001", "cookiemon", "111-111-1111")]
      results = get_orders_by_customer("cookiemon")
      assert results == expected_results


  def test_orders_by_customer_shipped_only():
      results = get_orders_by_customer("cookiemon", True)
      assert results == []


  def test_orders_by_customer_unshipped_only():
      expected_results = [("wlk001", "cookiemon", "111-111-1111")]
      results = get_orders_by_customer("cookiemon", False)
      assert results == expected_results


  def test_orders_by_customer_with_details():
      expected_results = [
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "dark chocolate chip",
              2,
              Decimal("1.00"),
          ),
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "oatmeal raisin",
              12,
              Decimal("3.00"),
          ),
      ]
      results = get_orders_by_customer("cookiemon", details=True)
      assert results == expected_results


  def test_orders_by_customer_shipped_only_with_details():
      results = get_orders_by_customer("cookiemon", True, True)
      assert results == []


  def test_orders_by_customer_unshipped_only_details():
      expected_results = [
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "dark chocolate chip",
              2,
              Decimal("1.00"),
          ),
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "oatmeal raisin",
              12,
              Decimal("3.00"),
          ),
      ]
      results = get_orders_by_customer("cookiemon", False, True)
      assert results == expected_results
  from decimal import Decimal
  from unittest import mock

  import pytest

  from app import get_orders_by_customer
  from db import dal, prep_db


  # --- общие тестовые данные ---

  @pytest.fixture
  def cookie_orders():
      return [("wlk001", "cookiemon", "111-111-1111")]


  @pytest.fixture
  def cookie_details():
      return [
          (
              "wlk001",
              "cookiemon",
              "111-111-1111",
              "dark chocolate chip",
              2,
              Decimal("1.00"),
          ),
          ("wlk001", "cookiemon", "111-111-1111", "oatmeal raisin", 12, Decimal("3.00")),
      ]


  # --- тесты ---

  @mock.patch("app.select")
  @mock.patch("app.dal.connection")
  def test_orders_by_customer_blank(mock_conn, mock_select):
      mock_select.return_value.select_from.return_value.where.return_value = None
      mock_conn.execute.return_value.fetchall.return_value = []

      results = get_orders_by_customer("")

      assert results == []


  @mock.patch("app.dal.connection")
  def test_orders_by_customer_blank_shipped(mock_conn):
      mock_conn.execute.return_value.fetchall.return_value = []

      results = get_orders_by_customer("", True)

      assert results == []


  @mock.patch("app.dal.connection")
  def test_orders_by_customer(mock_conn, cookie_orders):
      mock_conn.execute.return_value.fetchall.return_value = cookie_orders

      results = get_orders_by_customer("cookiemon")

      assert results == cookie_orders

Chapter 5: Reflection

SQLAlchemy v2

  from sqlalchemy import MetaData, create_engine
  metadata = MetaData()
  engine = create_engine("sqlite:///Chinook_Sqlite.sqlite")
  from sqlalchemy import Table

  artist = Table("Artist", metadata, autoload_with=engine)
  artist.columns.keys()
ArtistId Name
  from sqlalchemy import select

  with engine.begin() as connection:
      s = select(artist).limit(10)
      result = connection.execute(s).fetchall()
  result
1 AC/DC
2 Accept
3 Aerosmith
4 Alanis Morissette
5 Alice In Chains
6 Antônio Carlos Jobim
7 Apocalyptica
8 Audioslave
9 BackBeat
10 Billy Cobham
  album = Table('Album', metadata, autoload_with=engine)
  res = metadata.tables['Album']
  repr(res)
Table('Album', MetaData(), Column('AlbumId', INTEGER(), table=<Album>, primary_key=True, nullable=False), Column('Title', NVARCHAR(length=160), table=<Album>, nullable=False), Column('ArtistId', INTEGER(), ForeignKey('Artist.ArtistId'), table=<Album>, nullable=False), schema=None)
  album.foreign_keys
{ForeignKey('Artist.ArtistId')}

Reflecting a Whole Database

  metadata.reflect(bind=engine)
  list(metadata.tables.keys())
Artist Album Customer Employee Genre Invoice InvoiceLine Track MediaType Playlist PlaylistTrack

Chapter 6: Defining Schema with SQLAlchemy ORM

SQLAlchemy v1

  import re
  import logging
  from ruff_format import format_string
  from sqlalchemy import Table, Column, Integer, Numeric, String
  from sqlalchemy.ext.declarative import declarative_base

  Base = declarative_base()


  class MyBase:
      @classmethod
      def display(cls: Base) -> str:
          raw = repr(cls.__table__).replace(f"<{cls.__tablename__}>", f'"{cls.__tablename__}"')
          raw = re.sub(
              r"CallableColumnDefault\(<function datetime\.now at 0x[0-9a-f]+>\)",
              "NOW",
              raw,
          )
          try:
              return format_string(raw)
          except Exception:
              logging.exception(raw)

      def __repr__(self) -> str:
          raw = f"{self.__class__.__name__}"
          columns = ','.join(
              f"{column.name}='{getattr(self, column.name)}'"
              for column in self.__table__.columns
          )
          return format_string(raw + '(' + columns + ')')


  class Cookie(Base, MyBase):
      __tablename__ = "cookies"

      cookie_id = Column(Integer(), primary_key=True)
      cookie_name = Column(String(50), index=True)
      cookie_recipe_url = Column(String(255))
      cookie_sku = Column(String(55))
      quantity = Column(Integer())
      unit_cost = Column(Numeric(12, 2))


  Cookie.display()
Table(
    "cookies",
    MetaData(),
    Column(
        "cookie_id",
        Integer(),
        table="cookies",
        primary_key=True,
        nullable=False,
    ),
    Column("cookie_name", String(length=50), table="cookies"),
    Column("cookie_recipe_url", String(length=255), table="cookies"),
    Column("cookie_sku", String(length=55), table="cookies"),
    Column("quantity", Integer(), table="cookies"),
    Column("unit_cost", Numeric(precision=12, scale=2), table="cookies"),
    schema=None,
)
Table(
    "cookies",
    MetaData(),
    Column(
        "cookie_id",
        Integer(),
        table="cookies",
        primary_key=True,
        nullable=False,
    ),
    Column("cookie_name", String(length=50), table="cookies"),
    Column("cookie_recipe_url", String(length=255), table="cookies"),
    Column("cookie_sku", String(length=55), table="cookies"),
    Column("quantity", Integer(), table="cookies"),
    Column("unit_cost", Numeric(precision=12, scale=2), table="cookies"),
    schema=None,
)
  from datetime import datetime
  from sqlalchemy import DateTime

  class User(Base, MyBase):
      __tablename__ = "users"

      user_id = Column(Integer(), primary_key=True)
      username = Column(String(12), nullable=False, unique=True)
      email_address = Column(String(255), nullable=False)
      phone = Column(String(20), nullable=False)
      password = Column(String(25), nullable=False)
      created_on = Column(DateTime(), default=datetime.now)
      updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)

  User.display()
Table(
    "users",
    MetaData(),
    Column(
        "user_id", Integer(), table="users", primary_key=True, nullable=False
    ),
    Column("username", String(length=12), table="users", nullable=False),
    Column("email_address", String(length=255), table="users", nullable=False),
    Column("phone", String(length=20), table="users", nullable=False),
    Column("password", String(length=25), table="users", nullable=False),
    Column("created_on", DateTime(), table="users", default=NOW),
    Column("updated_on", DateTime(), table="users", onupdate=NOW, default=NOW),
    schema=None,
)
Table(
    "users",
    MetaData(),
    Column(
        "user_id", Integer(), table="users", primary_key=True, nullable=False
    ),
    Column("username", String(length=12), table="users", nullable=False),
    Column("email_address", String(length=255), table="users", nullable=False),
    Column("phone", String(length=20), table="users", nullable=False),
    Column("password", String(length=25), table="users", nullable=False),
    Column("created_on", DateTime(), table="users", default=NOW),
    Column("updated_on", DateTime(), table="users", onupdate=NOW, default=NOW),
    schema=None,
)

Keys, Constraints, and Indexes

  class SomeDataClass(Base):
      __tablename__ = "somedataclass"
      __table_args__ = (
          ForeignKeyConstraint("id", "other_table.id"),
          CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
      )

Relationships

  from sqlalchemy import ForeignKey, Boolean
  from sqlalchemy.orm import relationship, backref


  class Order(Base, MyBase):
      __tablename__ = "orders"

      order_id = Column(Integer(), primary_key=True)
      user_id = Column(Integer(), ForeignKey("users.user_id"))
      shipped = Column(Boolean(), default=False)

      user = relationship("User", backref=backref("orders", order_by=order_id))


  Order.display()
Table(
    "orders",
    MetaData(),
    Column(
        "order_id", Integer(), table="orders", primary_key=True, nullable=False
    ),
    Column("user_id", Integer(), ForeignKey("users.user_id"), table="orders"),
    Column(
        "shipped",
        Boolean(),
        table="orders",
        default=ScalarElementColumnDefault(False),
    ),
    schema=None,
)
Table(
    "orders",
    MetaData(),
    Column(
        "order_id", Integer(), table="orders", primary_key=True, nullable=False
    ),
    Column("user_id", Integer(), ForeignKey("users.user_id"), table="orders"),
    Column(
        "shipped",
        Boolean(),
        table="orders",
        default=ScalarElementColumnDefault(False),
    ),
    schema=None,
)

One-to-One

  class LineItem(Base, MyBase):
      __tablename__ = "line_items"

      line_item_id = Column(Integer(), primary_key=True)
      order_id = Column(Integer(), ForeignKey("orders.order_id"))
      cookie_id = Column(Integer(), ForeignKey("cookies.cookie_id"))
      quantity = Column(Integer())
      extended_cost = Column(Numeric(12, 2))
      order = relationship("Order", backref=backref("line_items", order_by=line_item_id))
      cookie = relationship("Cookie", uselist=False)

  LineItem.display()
Table(
    "line_items",
    MetaData(),
    Column(
        "line_item_id",
        Integer(),
        table="line_items",
        primary_key=True,
        nullable=False,
    ),
    Column(
        "order_id", Integer(), ForeignKey("orders.order_id"), table="line_items"
    ),
    Column(
        "cookie_id",
        Integer(),
        ForeignKey("cookies.cookie_id"),
        table="line_items",
    ),
    Column("quantity", Integer(), table="line_items"),
    Column("extended_cost", Numeric(precision=12, scale=2), table="line_items"),
    schema=None,
)
Table(
    "line_items",
    MetaData(),
    Column(
        "line_item_id",
        Integer(),
        table="line_items",
        primary_key=True,
        nullable=False,
    ),
    Column(
        "order_id", Integer(), ForeignKey("orders.order_id"), table="line_items"
    ),
    Column(
        "cookie_id",
        Integer(),
        ForeignKey("cookies.cookie_id"),
        table="line_items",
    ),
    Column("quantity", Integer(), table="line_items"),
    Column("extended_cost", Numeric(precision=12, scale=2), table="line_items"),
    schema=None,
)
  Order.display()
Table(
    "orders",
    MetaData(),
    Column(
        "order_id", Integer(), table="orders", primary_key=True, nullable=False
    ),
    Column("user_id", Integer(), ForeignKey("users.user_id"), table="orders"),
    Column(
        "shipped",
        Boolean(),
        table="orders",
        default=ScalarElementColumnDefault(False),
    ),
    schema=None,
)
  from sqlalchemy import create_engine
  engine = create_engine("sqlite:///:memory:")

  Base.metadata.create_all(engine)

SQLAlchemy v2

  import re
  import logging
  from datetime import datetime
  from decimal import Decimal

  from ruff_format import format_string
  from sqlalchemy import (
      Boolean,
      DateTime,
      ForeignKey,
      Integer,
      Numeric,
      String,
      create_engine,
      select,
  )
  from sqlalchemy.orm import (
      DeclarativeBase,
      Mapped,
      mapped_column,
      relationship,
      Session,
  )


  class Base(DeclarativeBase):
      """Базовый класс для всех декларативных моделей."""


  class MyBase:
      """Миксин для красивого отображения таблицы."""

      @classmethod
      def display(cls) -> str:
          raw = repr(cls.__table__).replace(
              f"<{cls.__tablename__}>", f'"{cls.__tablename__}"'
          )
          raw = re.sub(
              r"CallableColumnDefault\(<function datetime\.now at 0x[0-9a-f]+>\)",
              "NOW",
              raw,
          )
          try:
              return format_string(raw)
          except Exception:
              logging.exception(raw)
              return raw


  class Cookie(Base, MyBase):
      __tablename__ = "cookies"

      cookie_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      cookie_name: Mapped[str | None] = mapped_column(String(50), index=True)
      cookie_recipe_url: Mapped[str | None] = mapped_column(String(255))
      cookie_sku: Mapped[str | None] = mapped_column(String(55))
      quantity: Mapped[int | None] = mapped_column(Integer)
      unit_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))


  Cookie.display()
Table(
    "cookies",
    MetaData(),
    Column(
        "cookie_id",
        Integer(),
        table="cookies",
        primary_key=True,
        nullable=False,
    ),
    Column("cookie_name", String(length=50), table="cookies"),
    Column("cookie_recipe_url", String(length=255), table="cookies"),
    Column("cookie_sku", String(length=55), table="cookies"),
    Column("quantity", Integer(), table="cookies"),
    Column("unit_cost", Numeric(precision=12, scale=2), table="cookies"),
    schema=None,
)
  class User(Base, MyBase):
      __tablename__ = "users"

      user_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      username: Mapped[str] = mapped_column(String(12), nullable=False, unique=True)
      email_address: Mapped[str] = mapped_column(String(255), nullable=False)
      phone: Mapped[str] = mapped_column(String(20), nullable=False)
      password: Mapped[str] = mapped_column(String(25), nullable=False)
      created_on: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
      updated_on: Mapped[datetime] = mapped_column(
          DateTime, default=datetime.now, onupdate=datetime.now
      )

      orders: Mapped[list["Order"]] = relationship(
          back_populates="user", order_by="Order.order_id"
      )


  User.display()
Table(
    "users",
    MetaData(),
    Column(
        "user_id", Integer(), table="users", primary_key=True, nullable=False
    ),
    Column("username", String(length=12), table="users", nullable=False),
    Column("email_address", String(length=255), table="users", nullable=False),
    Column("phone", String(length=20), table="users", nullable=False),
    Column("password", String(length=25), table="users", nullable=False),
    Column(
        "created_on", DateTime(), table="users", nullable=False, default=NOW
    ),
    Column(
        "updated_on",
        DateTime(),
        table="users",
        nullable=False,
        onupdate=NOW,
        default=NOW,
    ),
    schema=None,
)

Keys, Constraints, and Indexes

  from sqlalchemy import CheckConstraint, ForeignKeyConstraint


  class SomeDataClass(Base):
      __tablename__ = "somedataclass"
      __table_args__ = (
          ForeignKeyConstraint(["id"], ["other_table.id"]),
          CheckConstraint("unit_cost >= 0.00", name="unit_cost_positive"),
      )

      id: Mapped[int] = mapped_column(Integer, primary_key=True)
      unit_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))

Relationships

  class Order(Base, MyBase):
      __tablename__ = "orders"

      order_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      user_id: Mapped[int | None] = mapped_column(ForeignKey("users.user_id"))
      shipped: Mapped[bool | None] = mapped_column(Boolean, default=False)

      user: Mapped["User | None"] = relationship(back_populates="orders")
      line_items: Mapped[list["LineItem"]] = relationship(
          back_populates="order", order_by="LineItem.line_item_id"
      )


  Order.display()
Table(
    "orders",
    MetaData(),
    Column(
        "order_id", Integer(), table="orders", primary_key=True, nullable=False
    ),
    Column("user_id", Integer(), ForeignKey("users.user_id"), table="orders"),
    Column(
        "shipped",
        Boolean(),
        table="orders",
        default=ScalarElementColumnDefault(False),
    ),
    schema=None,
)

One-to-One

  class LineItem(Base, MyBase):
      __tablename__ = "line_items"

      line_item_id: Mapped[int] = mapped_column(Integer, primary_key=True)
      order_id: Mapped[int | None] = mapped_column(ForeignKey("orders.order_id"))
      cookie_id: Mapped[int | None] = mapped_column(ForeignKey("cookies.cookie_id"))
      quantity: Mapped[int | None] = mapped_column(Integer)
      extended_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))

      order: Mapped["Order | None"] = relationship(back_populates="line_items")
      cookie: Mapped["Cookie | None"] = relationship(uselist=False)


  LineItem.display()
Table(
    "line_items",
    MetaData(),
    Column(
        "line_item_id",
        Integer(),
        table="line_items",
        primary_key=True,
        nullable=False,
    ),
    Column(
        "order_id", Integer(), ForeignKey("orders.order_id"), table="line_items"
    ),
    Column(
        "cookie_id",
        Integer(),
        ForeignKey("cookies.cookie_id"),
        table="line_items",
    ),
    Column("quantity", Integer(), table="line_items"),
    Column("extended_cost", Numeric(precision=12, scale=2), table="line_items"),
    schema=None,
)
  Order.display()
Table(
    "orders",
    MetaData(),
    Column(
        "order_id", Integer(), table="orders", primary_key=True, nullable=False
    ),
    Column("user_id", Integer(), ForeignKey("users.user_id"), table="orders"),
    Column(
        "shipped",
        Boolean(),
        table="orders",
        default=ScalarElementColumnDefault(False),
    ),
    schema=None,
)
  engine = create_engine("sqlite:///:memory:")

  Base.metadata.create_all(engine)

  with Session(engine) as session:
      session.add(
          Cookie(
              cookie_name="chocolate chip",
              cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
              cookie_sku="CC01",
              quantity=12,
              unit_cost=Decimal("0.50"),
          )
      )
      session.commit()

      stmt = select(Cookie).where(Cookie.cookie_name == "chocolate chip")
      for cookie in session.scalars(stmt):
          print(cookie.cookie_name, cookie.unit_cost)

Chapter 7: Working with Data via SQLAlchemy ORM

SQLAlchemy v1

  from sqlalchemy import create_engine
  from sqlalchemy.orm import sessionmaker

  engine = create_engine("sqlite:///:memory:")
  Session = sessionmaker(bind=engine)
  session = Session()
  <<MyBase>>
  <<User>>
  <<Order>>
  <<LineItem>>

  Base.metadata.create_all(engine)

Inserting Data

  cc_cookie = Cookie(
      cookie_name="chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
      cookie_sku="CC01",
      quantity="12",
      unit_cost=0.50,
  )
  session.add(cc_cookie)
  session.commit()
  cc_cookie.cookie_id
1
  dcc = Cookie(
      cookie_name="dark chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe/recipe_dark.html",
      cookie_sku="CC02",
      quantity=1,
      unit_cost=0.75,
  )
  mol = Cookie(
      cookie_name="molasses",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe_molasses.html",
      cookie_sku="MOL01",
      quantity=1,
      unit_cost=0.80,
  )
  session.add(dcc)
  session.add(mol)
  session.flush()
  [('dcc.cookie_id', dcc.cookie_id), ('mol.cookie_id', mol.cookie_id)]
dcc.cookie_id 2
mol.cookie_id 3
  c1 = Cookie(
      cookie_name="peanut butter",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe/peanut.html",
      cookie_sku="PB01",
      quantity=24,
      unit_cost=0.25,
  )
  c2 = Cookie(
      cookie_name="oatmeal raisin",
      cookie_recipe_url="http://some.okay.me/cookie/raisin.html",
      cookie_sku="EWW01",
      quantity=100,
      unit_cost=1.00,
  )
  session.bulk_save_objects([c1, c2])
  session.commit()
  # Тут будет None, так как для скорости делается только вставка данных,
  # обратно ничего не запрашивается и поэтому не добавляется в объекты
  c1.cookie_id
None

Querying Data

  cookies = session.query(Cookie).all()
  print(cookies)
[Cookie(
    cookie_id="1",
    cookie_name="chocolate chip",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
    cookie_sku="CC01",
    quantity="12",
    unit_cost="0.50",
)
, Cookie(
    cookie_id="2",
    cookie_name="dark chocolate chip",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe/recipe_dark.html",
    cookie_sku="CC02",
    quantity="1",
    unit_cost="0.75",
)
, Cookie(
    cookie_id="3",
    cookie_name="molasses",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe_molasses.html",
    cookie_sku="MOL01",
    quantity="1",
    unit_cost="0.80",
)
, Cookie(
    cookie_id="4",
    cookie_name="peanut butter",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe/peanut.html",
    cookie_sku="PB01",
    quantity="24",
    unit_cost="0.25",
)
, Cookie(
    cookie_id="5",
    cookie_name="oatmeal raisin",
    cookie_recipe_url="http://some.okay.me/cookie/raisin.html",
    cookie_sku="EWW01",
    quantity="100",
    unit_cost="1.00",
)
]
Controlling the Columns in the Query
  session.query(Cookie.cookie_name, Cookie.quantity).first()
chocolate chip 12
Ordering
  results = [('quantity', 'cookie_name')]

  for cookie in session.query(Cookie).order_by(Cookie.quantity):
      results.append((cookie.quantity, cookie.cookie_name))
  results
quantity cookie_name
1 dark chocolate chip
1 molasses
12 chocolate chip
24 peanut butter
100 oatmeal raisin
  import pandas as pd
  from sqlalchemy import desc
  results = {'quantity': [], 'cookie_name': []}

  for cookie in session.query(Cookie).order_by(desc(Cookie.quantity)):
      results['quantity'].append(cookie.quantity)
      results['cookie_name'].append(cookie.cookie_name)
  df = pd.DataFrame(results)
  # Преобразуем DataFrame в список списков для Org Babel
  # 1. Заголовки: берем названия колонок и добавляем пустую ячейку для индекса
  headers = [''] + df.columns.tolist()

  # 2. Данные: объединяем индекс и значения строк
  rows = [[idx] + row.tolist() for idx, row in df.iterrows()]

  # 3. Собираем всё вместе: заголовки + разделитель (None) + строки
  summary = [headers] + [None] + rows
  summary
quantity cookie_name
0 100 oatmeal raisin
1 24 peanut butter
2 12 chocolate chip
3 1 dark chocolate chip
4 1 molasses
Limiting
  query = session.query(Cookie).order_by(Cookie.quantity)[:2]
  # query = session.query(Cookie).order_by(Cookie.quantity).limit(2)
  [(result.cookie_name,) for result in query]
dark chocolate chip
molasses
Functions and Labels
  from sqlalchemy import func

  inv_count = session.query(func.sum(Cookie.quantity)).scalar()
  inv_count
138
  rec_count = session.query(func.count(Cookie.cookie_name)).first()
  rec_count

(5,)

  rec_count = session.query(
      func.count(Cookie.cookie_name).label("inventory_count")
  ).first()
  (list(rec_count._mapping.keys()), (rec_count.inventory_count, ))
inventory_count
5
Filtering
  record = session.query(Cookie).filter(Cookie.cookie_name == "chocolate chip").first()
  record
Cookie(
    cookie_id="1",
    cookie_name="chocolate chip",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
    cookie_sku="CC01",
    quantity="12",
    unit_cost="0.50",
)
  record = session.query(Cookie).filter_by(cookie_name="chocolate chip").first()
  record
Cookie(
    cookie_id="1",
    cookie_name="chocolate chip",
    cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
    cookie_sku="CC01",
    quantity="12",
    unit_cost="0.50",
)
  query = session.query(Cookie).filter(Cookie.cookie_name.like("chocolate chip"))
  [record.cookie_name for record in query]
chocolate chip
Operators
  results = session.query(Cookie.cookie_name, "SKU-" + Cookie.cookie_sku).all()
  [row for row in results]
chocolate chip SKU-CC01
dark chocolate chip SKU-CC02
molasses SKU-MOL01
peanut butter SKU-PB01
oatmeal raisin SKU-EWW01
  from sqlalchemy import cast

  query = session.query(
      Cookie.cookie_name,
      cast((Cookie.quantity * Cookie.unit_cost), Numeric(12, 2)).label("inv_cost"),
  )
  [(result.cookie_name, repr(result.inv_cost)) for result in query]
chocolate chip Decimal('6.00')
dark chocolate chip Decimal('0.75')
molasses Decimal('0.80')
peanut butter Decimal('6.00')
oatmeal raisin Decimal('100.00')
Conjunctions
  query = session.query(Cookie).filter(Cookie.quantity > 23, Cookie.unit_cost < 0.40)
  [(result.cookie_name,) for result in query]
peanut butter
  from sqlalchemy import and_, or_, not_

  query = session.query(Cookie).filter(
      or_(Cookie.quantity.between(10, 50), Cookie.cookie_name.contains("chip"))
  )

  [(result.cookie_name,) for result in query]
chocolate chip
dark chocolate chip
peanut butter

Updating Data

  query = session.query(Cookie)
  cc_cookie = query.filter(Cookie.cookie_name == "chocolate chip").first()
  cc_cookie.quantity = cc_cookie.quantity + 120
  session.commit()
  cc_cookie.quantity
132
  query = session.query(Cookie)
  query = query.filter(Cookie.cookie_name == "chocolate chip")
  query.update({Cookie.quantity: Cookie.quantity - 20})
  cc_cookie = query.first()
  cc_cookie.quantity
112

Deleting Data

  query = session.query(Cookie)
  query = query.filter(Cookie.cookie_name == "dark chocolate chip")
  # Гарантируем, что такая запись только одна,
  # и только она одна будет удалена
  dcc_cookie = query.one()
  session.delete(dcc_cookie)
  session.commit()
  dcc_cookie = query.first()
  # Здесь должен быть None
  assert dcc_cookie is None
None
  query = session.query(Cookie)
  query = query.filter(Cookie.cookie_name == "molasses")
  query.delete()
  # Убедимся, что больше такой записи нет
  mol_cookie = query.first()
  # Вернёт None
  assert mol_cookie is None
None

Relations

  cookiemon = User(
      username="cookiemon",
      email_address="mon@cookie.com",
      phone="111-111-1111",
      password="password",
  )
  cakeeater = User(
      username="cakeeater",
      email_address="cakeeater@cake.com",
      phone="222-222-2222",
      password="password",
  )
  pieperson = User(
      username="pieperson",
      email_address="person@pie.com",
      phone="333-333-3333",
      password="password",
  )
  session.add(cookiemon)
  session.add(cakeeater)
  session.add(pieperson)
  session.commit()
  o1 = Order()
  o1.user = cookiemon
  session.add(o1)
  cc = session.query(Cookie).filter(Cookie.cookie_name == "chocolate chip").one()
  line1 = LineItem(cookie=cc, quantity=2, extended_cost=1.00)
  pb = session.query(Cookie).filter(Cookie.cookie_name == "peanut butter").one()
  line2 = LineItem(quantity=12, extended_cost=3.00)
  line2.cookie = pb
  line2.order = o1
  o1.line_items.append(line1)
  o1.line_items.append(line2)
  session.commit()
  o2 = Order()
  o2.user = cakeeater
  cc = session.query(Cookie).filter(Cookie.cookie_name == "chocolate chip").one()
  line1 = LineItem(cookie=cc, quantity=24, extended_cost=12.00)
  oat = session.query(Cookie).filter(Cookie.cookie_name == "oatmeal raisin").one()
  line2 = LineItem(cookie=oat, quantity=6, extended_cost=6.00)
  o2.line_items.append(line1)
  o2.line_items.append(line2)
  session.add(o2)
  session.commit()

Joins

  query = (
      session.query(
          Order.order_id,
          User.username,
          User.phone,
          Cookie.cookie_name,
          LineItem.quantity,
          LineItem.extended_cost,
      )
      .join(User)
      .join(LineItem)
      .join(Cookie)
      .filter(User.username == "cookiemon")
  )
  [row for row in query]
1 cookiemon 111-111-1111 chocolate chip 2 Decimal (1.00)
1 cookiemon 111-111-1111 peanut butter 12 Decimal (3.00)

Grouping

  query = (
      session.query(User.username, func.count(Order.order_id))
      .outerjoin(Order)
      .group_by(User.username)
      .all()
  )
  query
cakeeater 1
cookiemon 1
pieperson 0

Chaining

  def get_orders_by_customer(
      cust_name: str, shipped: bool | None = None, details: bool = False
  ):
      query = session.query(Order.order_id, User.username, User.phone).join(User)
      if details:
          query = query.add_columns(
              Cookie.cookie_name, LineItem.quantity, LineItem.extended_cost
          )
          query = query.join(LineItem).join(Cookie)
      if shipped is not None:
          query = query.where(Order.shipped == shipped)
      results = query.filter(User.username == cust_name).all()
      return results


  get_orders_by_customer("cakeeater")
2 cakeeater 222-222-2222
  get_orders_by_customer("cakeeater", details=True)
2 cakeeater 222-222-2222 chocolate chip 24 Decimal (12.00)
2 cakeeater 222-222-2222 oatmeal raisin 6 Decimal (6.00)
  get_orders_by_customer("cakeeater", shipped=True)

[]

  get_orders_by_customer("cakeeater", shipped=False)
2 cakeeater 222-222-2222
  get_orders_by_customer("cakeeater", shipped=False, details=True)
2 cakeeater 222-222-2222 chocolate chip 24 Decimal (12.00)
2 cakeeater 222-222-2222 oatmeal raisin 6 Decimal (6.00)

Raw Queries

  from sqlalchemy import text
  query = session.query(User).filter(text("username='cookiemon'"))
  for u in query.all():
      print(u)
User(
    user_id="1",
    username="cookiemon",
    email_address="mon@cookie.com",
    phone="111-111-1111",
    password="password",
    created_on="2026-09-21 10:55:18.029598",
    updated_on="2026-09-21 10:55:18.029607",
)

SQLAlchemy v2

  #!/usr/bin/env python3
  """Идиоматичный SQLAlchemy 2.0 пример (по мотивам «Essential SQLAlchemy»)."""

  import logging
  import re
  from datetime import datetime
  from decimal import Decimal

  import pandas as pd
  from ruff_format import format_string
  from sqlalchemy import (
      Boolean,
      DateTime,
      ForeignKey,
      Integer,
      Numeric,
      String,
      create_engine,
      delete,
      func,
      insert,
      or_,
      select,
      text,
      update,
  )
  from sqlalchemy.orm import (
      DeclarativeBase,
      Mapped,
      Session,
      mapped_column,
      relationship,
  )

  # ---------------------------------------------------------------------------
  # Engine
  # ---------------------------------------------------------------------------
  engine = create_engine("sqlite:///:memory:", echo=False)


  # ---------------------------------------------------------------------------
  # Базовый класс с удобным repr и display
  # ---------------------------------------------------------------------------
  class Base(DeclarativeBase):
      """Базовый класс с repr() и display() для схемы таблицы."""

      def __repr__(self) -> str:
          raw = f"{self.__class__.__name__}"
          columns = ",".join(
              f"{col.name}={getattr(self, col.name)!r}"
              for col in self.__table__.columns
          )
          return format_string(f"{raw}({columns})")

      @classmethod
      def display(cls) -> str:
          raw = repr(cls.__table__).replace(
              f"<{cls.__tablename__}>", f'"{cls.__tablename__}"'
          )
          raw = re.sub(
              r"CallableColumnDefault\(<function datetime\.now at 0x[0-9a-f]+>\)",
              "NOW",
              raw,
          )
          try:
              return format_string(raw)
          except Exception:
              logging.exception(raw)
              return raw


  # ---------------------------------------------------------------------------
  # Модели
  # ---------------------------------------------------------------------------
  class Cookie(Base):
      __tablename__ = "cookies"

      cookie_id: Mapped[int] = mapped_column(primary_key=True)
      cookie_name: Mapped[str | None] = mapped_column(String(50), index=True)
      cookie_recipe_url: Mapped[str | None] = mapped_column(String(255))
      cookie_sku: Mapped[str | None] = mapped_column(String(55))
      quantity: Mapped[int | None] = mapped_column(Integer())
      unit_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))


  class User(Base):
      __tablename__ = "users"

      user_id: Mapped[int] = mapped_column(primary_key=True)
      username: Mapped[str] = mapped_column(String(12), nullable=False, unique=True)
      email_address: Mapped[str] = mapped_column(String(255), nullable=False)
      phone: Mapped[str] = mapped_column(String(20), nullable=False)
      password: Mapped[str] = mapped_column(String(25), nullable=False)
      created_on: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
      updated_on: Mapped[datetime] = mapped_column(
          DateTime, default=datetime.now, onupdate=datetime.now
      )


  class Order(Base):
      __tablename__ = "orders"

      order_id: Mapped[int] = mapped_column(primary_key=True)
      user_id: Mapped[int | None] = mapped_column(ForeignKey("users.user_id"))
      shipped: Mapped[bool] = mapped_column(Boolean, default=False)

      user: Mapped["User"] = relationship(back_populates="orders")
      line_items: Mapped[list["LineItem"]] = relationship(back_populates="order")


  class LineItem(Base):
      __tablename__ = "line_items"

      line_item_id: Mapped[int] = mapped_column(primary_key=True)
      order_id: Mapped[int | None] = mapped_column(ForeignKey("orders.order_id"))
      cookie_id: Mapped[int | None] = mapped_column(ForeignKey("cookies.cookie_id"))
      quantity: Mapped[int | None] = mapped_column(Integer())
      extended_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))

      order: Mapped["Order"] = relationship(back_populates="line_items")
      cookie: Mapped["Cookie"] = relationship(uselist=False)


  # Дополняем User.orders после объявления Order (back_populates с обеих сторон)
  User.orders = relationship("Order", back_populates="user", order_by="Order.order_id")


  # ---------------------------------------------------------------------------
  # Создание схемы
  # ---------------------------------------------------------------------------
  Base.metadata.create_all(engine)

  Cookie.display()
  User.display()
  Order.display()
  LineItem.display()


  # ---------------------------------------------------------------------------
  # Работа с сессией
  # ---------------------------------------------------------------------------
  with Session(engine) as session:
      # =======================================================================
      # INSERT: одиночные объекты через ORM
      # =======================================================================
      cc_cookie = Cookie(
          cookie_name="chocolate chip",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
          cookie_sku="CC01",
          quantity=12,
          unit_cost=Decimal("0.50"),
      )
      session.add(cc_cookie)
      session.commit()
      print("cc_cookie.cookie_id =", cc_cookie.cookie_id)

      dcc = Cookie(
          cookie_name="dark chocolate chip",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe/recipe_dark.html",
          cookie_sku="CC02",
          quantity=1,
          unit_cost=Decimal("0.75"),
      )
      mol = Cookie(
          cookie_name="molasses",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe_molasses.html",
          cookie_sku="MOL01",
          quantity=1,
          unit_cost=Decimal("0.80"),
      )
      session.add_all([dcc, mol])
      session.flush()
      print([("dcc.cookie_id", dcc.cookie_id), ("mol.cookie_id", mol.cookie_id)])

      # =======================================================================
      # INSERT: bulk-вставка (замена legacy bulk_save_objects)
      # =======================================================================
      session.execute(
          insert(Cookie),
          [
              {
                  "cookie_name": "peanut butter",
                  "cookie_recipe_url": "http://some.aweso.me/cookie/recipe/peanut.html",
                  "cookie_sku": "PB01",
                  "quantity": 24,
                  "unit_cost": Decimal("0.25"),
              },
              {
                  "cookie_name": "oatmeal raisin",
                  "cookie_recipe_url": "http://some.okay.me/cookie/raisin.html",
                  "cookie_sku": "EWW01",
                  "quantity": 100,
                  "unit_cost": Decimal("1.00"),
              },
          ],
      )
      session.commit()

      # Пример с .returning() — если нужны обратно PK/имена
      result = session.execute(
          insert(Cookie).returning(Cookie.cookie_id, Cookie.cookie_name),
          [
              {
                  "cookie_name": "sugar",
                  "cookie_recipe_url": "http://example.com/sugar",
                  "cookie_sku": "SGR01",
                  "quantity": 50,
                  "unit_cost": Decimal("0.40"),
              },
          ],
      )
      print("inserted (returning):", result.all())
      session.commit()

      # =======================================================================
      # SELECT: базовые
      # =======================================================================
      cookies = session.scalars(select(Cookie)).all()
      print(cookies)

      row = session.execute(select(Cookie.cookie_name, Cookie.quantity)).first()
      print(row)

      # =======================================================================
      # DataFrame из select()
      # =======================================================================
      stmt = select(Cookie.quantity, Cookie.cookie_name).order_by(Cookie.quantity.desc())
      rows = session.execute(stmt).all()
      df = pd.DataFrame(rows, columns=["quantity", "cookie_name"])

      headers = [""] + df.columns.tolist()
      data_rows = [[idx, *row] for idx, row in zip(df.index, df.to_numpy().tolist())]
      summary = [headers, None, *data_rows]
      print(summary)

      # =======================================================================
      # LIMIT
      # =======================================================================
      stmt = select(Cookie).order_by(Cookie.quantity).limit(2)
      print([(c.cookie_name,) for c in session.scalars(stmt)])

      # =======================================================================
      # Агрегаты
      # =======================================================================
      inv_count = session.scalar(select(func.sum(Cookie.quantity)))
      print("inv_count =", inv_count)

      rec = session.execute(
          select(func.count(Cookie.cookie_name).label("inventory_count"))
      ).one()
      print((list(rec._mapping.keys()), (rec.inventory_count,)))

      # =======================================================================
      # Фильтры
      # =======================================================================
      record = session.scalars(
          select(Cookie).where(Cookie.cookie_name == "chocolate chip")
      ).first()
      print(record)

      record = session.scalars(
          select(Cookie).filter_by(cookie_name="chocolate chip")
      ).first()
      print(record)

      stmt = select(Cookie).where(Cookie.cookie_name.like("chocolate chip"))
      print([c.cookie_name for c in session.scalars(stmt)])

      # =======================================================================
      # Вычисляемые колонки
      # =======================================================================
      stmt = select(Cookie.cookie_name, ("SKU-" + Cookie.cookie_sku).label("sku"))
      for r in session.execute(stmt):
          print(r)

      stmt = select(
          Cookie.cookie_name,
          func.cast(Cookie.quantity * Cookie.unit_cost, Numeric(12, 2)).label("inv_cost"),
      )
      print([(r.cookie_name, repr(r.inv_cost)) for r in session.execute(stmt)])

      stmt = select(Cookie).where(
          Cookie.quantity > 23, Cookie.unit_cost < Decimal("0.40")
      )
      print([(c.cookie_name,) for c in session.scalars(stmt)])

      stmt = select(Cookie).where(
          or_(
              Cookie.quantity.between(10, 50),
              Cookie.cookie_name.contains("chip"),
          )
      )
      print([(c.cookie_name,) for c in session.scalars(stmt)])

      # =======================================================================
      # UPDATE (ORM-объект)
      # =======================================================================
      cc_cookie = session.scalars(
          select(Cookie).filter_by(cookie_name="chocolate chip")
      ).first()
      assert cc_cookie is not None
      cc_cookie.quantity = (cc_cookie.quantity or 0) + 120
      session.commit()
      print("cc_cookie.quantity =", cc_cookie.quantity)

      # UPDATE (bulk)
      session.execute(
          update(Cookie)
          .where(Cookie.cookie_name == "chocolate chip")
          .values(quantity=Cookie.quantity - 20)
      )
      session.commit()
      print(
          "after bulk update:",
          session.scalars(
              select(Cookie.quantity).where(Cookie.cookie_name == "chocolate chip")
          ).one(),
      )

      # =======================================================================
      # DELETE (ORM)
      # =======================================================================
      dcc_cookie = session.scalars(
          select(Cookie).filter_by(cookie_name="dark chocolate chip")
      ).one()
      session.delete(dcc_cookie)
      session.commit()
      assert (
          session.scalars(
              select(Cookie).filter_by(cookie_name="dark chocolate chip")
          ).first()
          is None
      )

      # DELETE (bulk)
      session.execute(delete(Cookie).where(Cookie.cookie_name == "molasses"))
      session.commit()
      assert (
          session.scalars(select(Cookie).filter_by(cookie_name="molasses")).first()
          is None
      )

      # =======================================================================
      # Пользователи и связи
      # =======================================================================
      cookiemon = User(
          username="cookiemon",
          email_address="mon@cookie.com",
          phone="111-111-1111",
          password="password",
      )
      cakeeater = User(
          username="cakeeater",
          email_address="cakeeater@cake.com",
          phone="222-222-2222",
          password="password",
      )
      pieperson = User(
          username="pieperson",
          email_address="person@pie.com",
          phone="333-333-3333",
          password="password",
      )
      session.add_all([cookiemon, cakeeater, pieperson])
      session.commit()

      o1 = Order()
      o1.user = cookiemon
      session.add(o1)

      cc = session.scalars(
          select(Cookie).filter_by(cookie_name="chocolate chip")
      ).one()
      pb = session.scalars(
          select(Cookie).filter_by(cookie_name="peanut butter")
      ).one()

      line1 = LineItem(cookie=cc, quantity=2, extended_cost=Decimal("1.00"))
      line2 = LineItem(quantity=12, extended_cost=Decimal("3.00"))
      line2.cookie = pb
      line2.order = o1
      o1.line_items.append(line1)
      o1.line_items.append(line2)
      session.commit()

      o2 = Order()
      o2.user = cakeeater
      line1 = LineItem(cookie=cc, quantity=24, extended_cost=Decimal("12.00"))
      oat = session.scalars(
          select(Cookie).filter_by(cookie_name="oatmeal raisin")
      ).one()
      line2 = LineItem(cookie=oat, quantity=6, extended_cost=Decimal("6.00"))
      o2.line_items.append(line1)
      o2.line_items.append(line2)
      session.add(o2)
      session.commit()

      # =======================================================================
      # JOIN
      # =======================================================================
      stmt = (
          select(
              Order.order_id,
              User.username,
              User.phone,
              Cookie.cookie_name,
              LineItem.quantity,
              LineItem.extended_cost,
          )
          .join(User)
          .join(LineItem)
          .join(Cookie)
          .where(User.username == "cookiemon")
      )
      print([tuple(r) for r in session.execute(stmt)])

      # =======================================================================
      # GROUP BY + OUTER JOIN
      # =======================================================================
      stmt = (
          select(User.username, func.count(Order.order_id))
          .outerjoin(Order)
          .group_by(User.username)
      )
      print(session.execute(stmt).all())

      # =======================================================================
      # Функция-обёртка
      # =======================================================================
      def get_orders_by_customer(
          cust_name: str,
          shipped: bool | None = None,
          details: bool = False,
      ):
          stmt = select(Order.order_id, User.username, User.phone).join(User)
          if details:
              stmt = (
                  stmt.add_columns(
                      Cookie.cookie_name,
                      LineItem.quantity,
                      LineItem.extended_cost,
                  )
                  .join(LineItem)
                  .join(Cookie)
              )
          if shipped is not None:
              stmt = stmt.where(Order.shipped == shipped)
          stmt = stmt.where(User.username == cust_name)
          return session.execute(stmt).all()

      print(get_orders_by_customer("cakeeater"))
      print(get_orders_by_customer("cakeeater", details=True))
      print(get_orders_by_customer("cakeeater", shipped=True))
      print(get_orders_by_customer("cakeeater", shipped=False))
      print(get_orders_by_customer("cakeeater", shipped=False, details=True))

      # =======================================================================
      # Текстовый SQL
      # =======================================================================
      stmt = select(User).where(text("username='cookiemon'"))
      for u in session.scalars(stmt):
          print(u)

Chapter 8: Understanding the Session and Exceptions

Transient
объект не находится в сессии, ровно как и не находится в базе данных
Pending
объект был добавлен в сессию методом add(), но ещё не был закоммичен
Persistent
объкт в сессии и содержит соответствующую запись в базе
Detached
объект больше не в сессии, но содержит запись в базе данных

SQLAlchemy v1

  from sqlalchemy import create_engine
  from sqlalchemy.orm import sessionmaker

  engine = create_engine("sqlite:///:memory:")
  Session = sessionmaker(bind=engine)
  session = Session()

  <<MyBase>>
  <<User>>
  <<Order>>
  <<LineItem>>

  Base.metadata.create_all(engine)

The SQLALchemy Session

  from sqlalchemy import inspect

  cc_cookie = Cookie(
          cookie_name="chocolate chip",
          cookie_recipe_url="http://some.aweso.me/cookie/recipe.html",
          cookie_sku="CC01",
          quantity="12",
          unit_cost=0.50,
  )
  insp = inspect(cc_cookie)
  [
      (state, getattr(insp, state))
      for state in ["transient", "pending", "persistent", "detached"]
  ]
transient True
pending False
persistent False
detached False
  session.add(cc_cookie)
  <<inspect_state>>
transient False
pending True
persistent False
detached False
  session.commit()
  <<inspect_state>>
transient False
pending False
persistent True
detached False
  session.expunge(cc_cookie)
  <<inspect_state>>
transient False
pending False
persistent False
detached True
  session.add(cc_cookie)
  cc_cookie.cookie_name = "Change chocolate chip"
  insp.modified
True
  for attr, attr_state in insp.attrs.items():
      if attr_state.history.has_changes():
          print('{}: {}'.format(attr, attr_state.value))
          print('History: {}\n'.format(attr_state.history))
cookie_name: Change chocolate chip
History: History(added=['Change chocolate chip'], unchanged=(), deleted=())

Exceptions

MultipleResultsFound
  import logging
  import sqlalchemy

  dcc = Cookie(
      cookie_name="dark chocolate chip",
      cookie_recipe_url="http://some.aweso.me/cookie/recipe_dark.html",
      cookie_sku="CC02",
      quantity="1",
      unit_cost=0.75,
  )
  session.add(dcc)
  session.commit()
  try:
      results = session.query(Cookie).one()
  except sqlalchemy.exc.MultipleResultsFound:
      logging.exception("более одной записи в таблице Cookie")
ERROR:root:более одной записи в таблице Cookie
Traceback (most recent call last):
  File "/tmp/babel-0NE78C/python-ZcRDHC", line 14, in <module>
    results = session.query(Cookie).one()
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/query.py", line 2815, in one
    return self._iter().one()  # type: ignore
           ~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 1867, in one
    return self._only_one_row(
           ~~~~~~~~~~~~~~~~~~^
        raise_for_second_row=True, raise_for_none=True, scalar=False
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 852, in _only_one_row
    raise exc.MultipleResultsFound(
    ...<4 lines>...
    )
sqlalchemy.exc.MultipleResultsFound: Multiple rows were found when exactly one was required
NoResultFound
  try:
      session.query(Order).one()
  except sqlalchemy.exc.NoResultFound:
      logging.exception("нет записей в таблице Order")
ERROR:root:нет записей в таблице Order
Traceback (most recent call last):
  File "/tmp/babel-0NE78C/python-gFJXyQ", line 2, in <module>
    session.query(Order).one()
    ~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/query.py", line 2815, in one
    return self._iter().one()  # type: ignore
           ~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 1867, in one
    return self._only_one_row(
           ~~~~~~~~~~~~~~~~~~^
        raise_for_second_row=True, raise_for_none=True, scalar=False
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/result.py", line 799, in _only_one_row
    raise exc.NoResultFound(
        "No row was found when one was required"
    )
sqlalchemy.exc.NoResultFound: No row was found when one was required
DetachedInstanceError
  cookiemon = User(
      username="cookiemon",
      email_address="mon@cookie.com",
      phone="111-111-1111",
      password="password",
  )
  session.add(cookiemon)
  o1 = Order()
  o1.user = cookiemon
  session.add(o1)

  cc = session.query(Cookie).filter(Cookie.cookie_name == "Change chocolate chip").one()
  line1 = LineItem(order=o1, cookie=cc, quantity=2, extended_cost=1.00)

  session.add(line1)
  session.commit()

  order = session.query(Order).first()
  session.expunge(order)

  try:
      order.line_items
  except sqlalchemy.orm.exc.DetachedInstanceError:
      logging.exception("order уже отвязан от сессии, нельзя получить его relationship")
ERROR:root:order уже отвязан от сессии, нельзя получить его relationship
Traceback (most recent call last):
  File "/tmp/babel-0NE78C/python-K8DLx5", line 22, in <module>
    order.line_items
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/attributes.py", line 569, in __get__
    return self.impl.get(state, dict_)  # type: ignore[no-any-return]
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/attributes.py", line 1096, in get
    value = self._fire_loader_callables(state, key, passive)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/attributes.py", line 1131, in _fire_loader_callables
    return self.callable_(state, passive)
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/strategies.py", line 929, in _load_for_state
    raise orm_exc.DetachedInstanceError(
    ...<3 lines>...
    )
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <Order at 0x7f47bd840c20> is not bound to a Session; lazy load operation of attribute 'line_items' cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)

Transactions

  import sqlalchemy as sa
  from sqlalchemy import create_engine
  from sqlalchemy.orm import sessionmaker

  engine = create_engine("sqlite:///:memory:")

  Session = sessionmaker(bind=engine)
  session = Session()

  from datetime import datetime

  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      Column,
      DateTime,
      ForeignKey,
      Integer,
      Numeric,
      String,
      Table,
  )
  from sqlalchemy.ext.declarative import declarative_base
  from sqlalchemy.orm import backref, relationship

  Base = declarative_base()


  class Cookie(Base):
      __tablename__ = "cookies"
      __table_args__ = (CheckConstraint("quantity >= 0", name="quantity_positive"),)
      cookie_id = Column(Integer, primary_key=True)
      cookie_name = Column(String(50), index=True)
      cookie_recipe_url = Column(String(255))
      cookie_sku = Column(String(55))
      quantity = Column(Integer())
      unit_cost = Column(Numeric(12, 2))

      def __init__(self, name, recipe_url=None, sku=None, quantity=0, unit_cost=0.00):
          self.cookie_name = name
          self.cookie_recipe_url = recipe_url
          self.cookie_sku = sku
          self.quantity = quantity
          self.unit_cost = unit_cost

      def __repr__(self):
          return (
              "Cookie(cookie_name='{self.cookie_name}', "
              "cookie_recipe_url='{self.cookie_recipe_url}', "
              "cookie_sku='{self.cookie_sku}', "
              "quantity={self.quantity}, "
              "unit_cost={self.unit_cost})".format(self=self)
          )


  class User(Base):
      __tablename__ = "users"
      user_id = Column(Integer(), primary_key=True)
      username = Column(String(15), nullable=False, unique=True)
      email_address = Column(String(255), nullable=False)
      phone = Column(String(20), nullable=False)
      password = Column(String(25), nullable=False)
      created_on = Column(DateTime(), default=datetime.now)
      updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)

      def __init__(self, username, email_address, phone, password):
          self.username = username
          self.email_address = email_address
          self.phone = phone
          self.password = password

      def __repr__(self):
          return (
              "User(username='{self.username}', "
              "email_address='{self.email_address}', "
              "phone='{self.phone}', "
              "password='{self.password}')".format(self=self)
          )


  class Order(Base):
      __tablename__ = "orders"
      order_id = Column(Integer(), primary_key=True)
      user_id = Column(Integer(), ForeignKey("users.user_id"))
      shipped = Column(Boolean(), default=False)
      user = relationship("User", backref=backref("orders", order_by=order_id))

      def __repr__(self):
          return "Order(user_id={self.user_id}, " "shipped={self.shipped})".format(
              self=self
          )


  class LineItem(Base):
      __tablename__ = "line_items"
      line_item_id = Column(Integer(), primary_key=True)
      order_id = Column(Integer(), ForeignKey("orders.order_id"))
      cookie_id = Column(Integer(), ForeignKey("cookies.cookie_id"))
      quantity = Column(Integer())
      extended_cost = Column(Numeric(12, 2))
      order = relationship("Order", backref=backref("line_items", order_by=line_item_id))
      cookie = relationship("Cookie", uselist=False)

      def __repr__(self):
          return (
              "LineItem(order_id={self.order_id}, "
              "cookie_id={self.cookie_id}, "
              "quantity={self.quantity}, "
              "extended_cost={self.extended_cost})".format(self=self)
          )


  Base.metadata.create_all(engine)
  cookiemon = User("cookiemon", "mon@cookie.com", "111-111-1111", "password")
  cc = Cookie(
      "chocolate chip", "http://some.aweso.me/cookie/recipe.html", "CC01", 12, 0.50
  )
  dcc = Cookie(
      "dark chocolate chip",
      "http://some.aweso.me/cookie/recipe_dark.html",
      "CC02",
      1,
      0.75,
  )
  session.add(cookiemon)
  session.add(cc)
  session.add(dcc)
  o1 = Order()
  o1.user = cookiemon
  session.add(o1)
  line1 = LineItem(order=o1, cookie=cc, quantity=9, extended_cost=4.50)
  session.add(line1)
  session.commit()
  o2 = Order()
  o2.user = cookiemon
  session.add(o2)
  line1 = LineItem(order=o2, cookie=cc, quantity=2, extended_cost=1.50)
  line2 = LineItem(order=o2, cookie=dcc, quantity=9, extended_cost=6.75)
  session.add(line1)
  session.add(line2)
  session.commit()


  def ship_it(order_id: str) -> None:
      """
      принимает order_id, удаляет cookies из доступного числа,
      помечает заказ как shipped
      """

      order = session.query(Order).get(order_id)
      for li in order.line_items:
          li.cookie.quantity = li.cookie.quantity - li.quantity
          session.add(li.cookie)
      order.shipped = True
      session.add(order)
      session.commit()
      print(f"shipped order ID: {order_id}")
  ship_it(1)
  print(session.query(Cookie.cookie_name, Cookie.quantity).all())
shipped order ID: 1
[('chocolate chip', 3), ('dark chocolate chip', 1)]
  import logging

  try:
      ship_it(2)
  except sa.exc.IntegrityError:
      logging.exception("order_id: 2")
ERROR:root:order_id: 2
Traceback (most recent call last):
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: CHECK constraint failed: quantity_positive

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/babel-0NE78C/python-y32EFG", line 4, in <module>
    ship_it(2)
    ~~~~~~~^^^
  File "<string>", line 156, in ship_it
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2035, in commit
    trans.commit(_to_root=True)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "<string>", line 2, in commit
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 1316, in commit
    self._prepare_impl()
    ~~~~~~~~~~~~~~~~~~^^
  File "<string>", line 2, in _prepare_impl
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 1290, in _prepare_impl
    self.session.flush()
    ~~~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4353, in flush
    self._flush(objects)
    ~~~~~~~~~~~^^^^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4488, in _flush
    with util.safe_reraise():
         ~~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/util/langhelpers.py", line 122, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4449, in _flush
    flush_context.execute()
    ~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/unitofwork.py", line 465, in execute
    rec.execute(self)
    ~~~~~~~~~~~^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/unitofwork.py", line 641, in execute
    util.preloaded.orm_persistence.save_obj(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self.mapper,
        ^^^^^^^^^^^^
        uow.states_for_mapper_hierarchy(self.mapper, False, False),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        uow,
        ^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/persistence.py", line 86, in save_obj
    _emit_update_statements(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        base_mapper,
        ^^^^^^^^^^^^
    ...<3 lines>...
        update,
        ^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/persistence.py", line 913, in _emit_update_statements
    c = connection.execute(
        statement, multiparams, execution_options=execution_options
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
    return meth(
        self,
        distilled_parameters,
        execution_options or NO_OPTIONS,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
    return connection._execute_clauseelement(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, distilled_params, execution_options
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
    ret = self._execute_context(
        dialect,
    ...<8 lines>...
        cache_hit=cache_hit,
    )
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
           ~~~~~~~~~~~~~~~~~~~~~~~~~^
        dialect, context, statement, parameters
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        e, str_statement, effective_parameters, cursor, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
    ~~~~~~~~~~~~~~~~~~~~~~~^
        cursor, str_statement, effective_parameters, context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
    cursor.execute(statement, parameters)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
[SQL: UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?]
[parameters: (-8, 2)]
(Background on this error at: https://sqlalche.me/e/20/gkpj)
  try:
      print(session.query(Cookie.cookie_name, Cookie.quantity).all())
  except sa.exc.InvalidRequestError:
      logging.exception("нужен rollback")
ERROR:root:нужен rollback
Traceback (most recent call last):
  File "/tmp/babel-0NE78C/python-aSjO6i", line 2, in <module>
    print(session.query(Cookie.cookie_name, Cookie.quantity).all())
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/query.py", line 2711, in all
    return self._iter().all()  # type: ignore
           ~~~~~~~~~~^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/query.py", line 2864, in _iter
    result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
                                                  ~~~~~~~~~~~~~~~~~~~~^
        statement,
        ^^^^^^^^^^
        params,
        ^^^^^^^
        execution_options={"_sa_orm_load_options": self.load_options},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2373, in execute
    return self._execute_internal(
           ~~~~~~~~~~~~~~~~~~~~~~^
        statement,
        ^^^^^^^^^^
    ...<4 lines>...
        _add_event=_add_event,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2261, in _execute_internal
    conn = self._connection_for_bind(bind)
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2113, in _connection_for_bind
    return trans._connection_for_bind(engine, execution_options)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 2, in _connection_for_bind
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 101, in _go
    self._raise_for_prerequisite_state(fn.__name__, current_state)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 975, in _raise_for_prerequisite_state
    raise sa_exc.PendingRollbackError(
    ...<6 lines>...
    )
sqlalchemy.exc.PendingRollbackError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
[SQL: UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?]
[parameters: (-8, 2)]
(Background on this error at: https://sqlalche.me/e/20/gkpj) (Background on this error at: https://sqlalche.me/e/20/7s2a)
  session.rollback()
  print(session.query(Cookie.cookie_name, Cookie.quantity).all())
[('chocolate chip', 3), ('dark chocolate chip', 1)]
  def trans_ship_it (order_id:str) -> None:
      """ship_it с поддержкой отката транзакции"""
      order = session.query(Order).get(order_id)
      for li in order.line_items:
          li.cookie.quantity = li.cookie.quantity - li.quantity
          session.add(li.cookie)
      order.shipped = True
      session.add(order)
      try:
          session.commit()
          print(f"shipped order ID: {order_id}")
      except sa.exc.IntegrityError as error:
          logging.error(error.orig)
          session.rollback()

  trans_ship_it(2)
  print(session.query(Cookie.cookie_name, Cookie.quantity).all())
ERROR:root:CHECK constraint failed: quantity_positive
[('chocolate chip', 3), ('dark chocolate chip', 1)]

SQLAlchemy v2

Transactions

  #!/usr/bin/env python3
  import logging
  from datetime import datetime
  from decimal import Decimal

  import sqlalchemy as sa
  from sqlalchemy import (
      Boolean,
      CheckConstraint,
      DateTime,
      ForeignKey,
      Integer,
      Numeric,
      String,
      create_engine,
      select,
  )
  from sqlalchemy.orm import (
      DeclarativeBase,
      Mapped,
      Session,
      mapped_column,
      relationship,
      sessionmaker,
  )

  engine = create_engine("sqlite:///:memory:")
  SessionLocal = sessionmaker(bind=engine)
  session = SessionLocal()


  class Base(DeclarativeBase):
      pass


  class Cookie(Base):
      __tablename__ = "cookies"
      __table_args__ = (CheckConstraint("quantity >= 0", name="quantity_positive"),)

      cookie_id: Mapped[int] = mapped_column(primary_key=True)
      cookie_name: Mapped[str] = mapped_column(String(50), index=True)
      cookie_recipe_url: Mapped[str | None] = mapped_column(String(255))
      cookie_sku: Mapped[str | None] = mapped_column(String(55))
      quantity: Mapped[int] = mapped_column(Integer(), default=0)
      unit_cost: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=Decimal("0.00"))

      def __init__(
          self,
          name: str,
          recipe_url: str | None = None,
          sku: str | None = None,
          quantity: int = 0,
          unit_cost: float | Decimal = 0.00,
      ) -> None:
          self.cookie_name = name
          self.cookie_recipe_url = recipe_url
          self.cookie_sku = sku
          self.quantity = quantity
          self.unit_cost = Decimal(str(unit_cost))

      def __repr__(self) -> str:
          return (
              f"Cookie(cookie_name='{self.cookie_name}', "
              f"cookie_recipe_url='{self.cookie_recipe_url}', "
              f"cookie_sku='{self.cookie_sku}', "
              f"quantity={self.quantity}, "
              f"unit_cost={self.unit_cost})"
          )


  class User(Base):
      __tablename__ = "users"

      user_id: Mapped[int] = mapped_column(primary_key=True)
      username: Mapped[str] = mapped_column(String(15), nullable=False, unique=True)
      email_address: Mapped[str] = mapped_column(String(255), nullable=False)
      phone: Mapped[str] = mapped_column(String(20), nullable=False)
      password: Mapped[str] = mapped_column(String(25), nullable=False)
      created_on: Mapped[datetime] = mapped_column(DateTime(), default=datetime.now)
      updated_on: Mapped[datetime] = mapped_column(
          DateTime(), default=datetime.now, onupdate=datetime.now
      )

      orders: Mapped[list["Order"]] = relationship(
          back_populates="user", order_by="Order.order_id"
      )

      def __init__(
          self, username: str, email_address: str, phone: str, password: str
      ) -> None:
          self.username = username
          self.email_address = email_address
          self.phone = phone
          self.password = password

      def __repr__(self) -> str:
          return (
              f"User(username='{self.username}', "
              f"email_address='{self.email_address}', "
              f"phone='{self.phone}', "
              f"password='{self.password}')"
          )


  class Order(Base):
      __tablename__ = "orders"

      order_id: Mapped[int] = mapped_column(primary_key=True)
      user_id: Mapped[int | None] = mapped_column(ForeignKey("users.user_id"))
      shipped: Mapped[bool] = mapped_column(Boolean(), default=False)

      user: Mapped["User | None"] = relationship(back_populates="orders")
      line_items: Mapped[list["LineItem"]] = relationship(
          back_populates="order", order_by="LineItem.line_item_id"
      )

      def __repr__(self) -> str:
          return f"Order(user_id={self.user_id}, shipped={self.shipped})"


  class LineItem(Base):
      __tablename__ = "line_items"

      line_item_id: Mapped[int] = mapped_column(primary_key=True)
      order_id: Mapped[int | None] = mapped_column(ForeignKey("orders.order_id"))
      cookie_id: Mapped[int | None] = mapped_column(ForeignKey("cookies.cookie_id"))
      quantity: Mapped[int] = mapped_column(Integer())
      extended_cost: Mapped[Decimal] = mapped_column(Numeric(12, 2))

      order: Mapped["Order | None"] = relationship(back_populates="line_items")
      cookie: Mapped["Cookie | None"] = relationship(uselist=False)

      def __repr__(self) -> str:
          return (
              f"LineItem(order_id={self.order_id}, "
              f"cookie_id={self.cookie_id}, "
              f"quantity={self.quantity}, "
              f"extended_cost={self.extended_cost})"
          )


  Base.metadata.create_all(engine)

  cookiemon = User("cookiemon", "mon@cookie.com", "111-111-1111", "password")
  cc = Cookie(
      "chocolate chip", "http://some.aweso.me/cookie/recipe.html", "CC01", 12, 0.50
  )
  dcc = Cookie(
      "dark chocolate chip",
      "http://some.aweso.me/cookie/recipe_dark.html",
      "CC02",
      1,
      0.75,
  )
  session.add(cookiemon)
  session.add(cc)
  session.add(dcc)

  o1 = Order()
  o1.user = cookiemon
  session.add(o1)
  line1 = LineItem(order=o1, cookie=cc, quantity=9, extended_cost=4.50)
  session.add(line1)
  session.commit()

  o2 = Order()
  o2.user = cookiemon
  session.add(o2)
  line1 = LineItem(order=o2, cookie=cc, quantity=2, extended_cost=1.50)
  line2 = LineItem(order=o2, cookie=dcc, quantity=9, extended_cost=6.75)
  session.add(line1)
  session.add(line2)
  session.commit()


  def ship_it(order_id: int) -> None:
      """
      принимает order_id, удаляет cookies из доступного числа,
      помечает заказ как shipped
      """
      order = session.get(Order, order_id)
      if order is None:
          raise ValueError(f"Order {order_id} not found")

      for li in order.line_items:
          assert li.cookie is not None
          li.cookie.quantity = li.cookie.quantity - li.quantity
          session.add(li.cookie)
      order.shipped = True
      session.add(order)
      session.commit()
      print(f"shipped order ID: {order_id}")


  ship_it(1)

  stmt = select(Cookie.cookie_name, Cookie.quantity)
  print(session.execute(stmt).all())

  try:
      ship_it(2)
  except sa.exc.IntegrityError:
      logging.exception("order_id: 2")

  try:
      print(session.execute(stmt).all())
  except sa.exc.InvalidRequestError:
      logging.exception("нужен rollback")

  session.rollback()
  print(session.execute(stmt).all())


  def trans_ship_it(order_id: int) -> None:
      try:
          with session.nested_begin():
              order = session.get(Order, order_id)
              if order is None:
                  raise ValueError(f"Order {order_id} not found")
              for li in order.line_items:
                  assert li.cookie is not None
                  li.cookie.quantity -= li.quantity
              order.shipped = True
          print(f"shipped order ID: {order_id}")
      except sa.exc.IntegrityError as error:
          logging.error(error.orig)


  trans_ship_it(2)
  print(session.execute(stmt).all())
  shipped order ID: 1
  [('chocolate chip', 3), ('dark chocolate chip', 1)]
  ERROR:root:order_id: 2
  Traceback (most recent call last):
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
      self.dialect.do_execute(
      ~~~~~~~~~~~~~~~~~~~~~~~^
          cursor, str_statement, effective_parameters, context
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
      cursor.execute(statement, parameters)
      ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  sqlite3.IntegrityError: CHECK constraint failed: quantity_positive

  The above exception was the direct cause of the following exception:

  Traceback (most recent call last):
    File "/tmp/babel-0NE78C/python-VGomxl", line 201, in <module>
      ship_it(2)
      ~~~~~~~^^^
    File "/tmp/babel-0NE78C/python-VGomxl", line 191, in ship_it
      session.commit()
      ~~~~~~~~~~~~~~^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2035, in commit
      trans.commit(_to_root=True)
      ~~~~~~~~~~~~^^^^^^^^^^^^^^^
    File "<string>", line 2, in commit
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
      ret_value = fn(self, *arg, **kw)
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 1316, in commit
      self._prepare_impl()
      ~~~~~~~~~~~~~~~~~~^^
    File "<string>", line 2, in _prepare_impl
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
      ret_value = fn(self, *arg, **kw)
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 1290, in _prepare_impl
      self.session.flush()
      ~~~~~~~~~~~~~~~~~~^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4353, in flush
      self._flush(objects)
      ~~~~~~~~~~~^^^^^^^^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4488, in _flush
      with util.safe_reraise():
           ~~~~~~~~~~~~~~~~~^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/util/langhelpers.py", line 122, in __exit__
      raise exc_value.with_traceback(exc_tb)
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 4449, in _flush
      flush_context.execute()
      ~~~~~~~~~~~~~~~~~~~~~^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/unitofwork.py", line 465, in execute
      rec.execute(self)
      ~~~~~~~~~~~^^^^^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/unitofwork.py", line 641, in execute
      util.preloaded.orm_persistence.save_obj(
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
          self.mapper,
          ^^^^^^^^^^^^
          uow.states_for_mapper_hierarchy(self.mapper, False, False),
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          uow,
          ^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/persistence.py", line 86, in save_obj
      _emit_update_statements(
      ~~~~~~~~~~~~~~~~~~~~~~~^
          base_mapper,
          ^^^^^^^^^^^^
      ...<3 lines>...
          update,
          ^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/persistence.py", line 913, in _emit_update_statements
      c = connection.execute(
          statement, multiparams, execution_options=execution_options
      )
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1421, in execute
      return meth(
          self,
          distilled_parameters,
          execution_options or NO_OPTIONS,
      )
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
      return connection._execute_clauseelement(
             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
          self, distilled_params, execution_options
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1643, in _execute_clauseelement
      ret = self._execute_context(
          dialect,
      ...<8 lines>...
          cache_hit=cache_hit,
      )
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
      return self._exec_single_context(
             ~~~~~~~~~~~~~~~~~~~~~~~~~^
          dialect, context, statement, parameters
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
      self._handle_dbapi_exception(
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
          e, str_statement, effective_parameters, cursor, context
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 2365, in _handle_dbapi_exception
      raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
      self.dialect.do_execute(
      ~~~~~~~~~~~~~~~~~~~~~~~^
          cursor, str_statement, effective_parameters, context
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute
      cursor.execute(statement, parameters)
      ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
  [SQL: UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?]
  [parameters: (-8, 2)]
  (Background on this error at: https://sqlalche.me/e/20/gkpj)
  ERROR:root:нужен rollback
  Traceback (most recent call last):
    File "/tmp/babel-0NE78C/python-VGomxl", line 206, in <module>
      print(session.execute(stmt).all())
            ~~~~~~~~~~~~~~~^^^^^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2373, in execute
      return self._execute_internal(
             ~~~~~~~~~~~~~~~~~~~~~~^
          statement,
          ^^^^^^^^^^
      ...<4 lines>...
          _add_event=_add_event,
          ^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2261, in _execute_internal
      conn = self._connection_for_bind(bind)
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 2113, in _connection_for_bind
      return trans._connection_for_bind(engine, execution_options)
             ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "<string>", line 2, in _connection_for_bind
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/state_changes.py", line 101, in _go
      self._raise_for_prerequisite_state(fn.__name__, current_state)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/home/octopus/study/Python/sqlalchemy-study/.venv/lib/python3.14/site-packages/sqlalchemy/orm/session.py", line 975, in _raise_for_prerequisite_state
      raise sa_exc.PendingRollbackError(
      ...<6 lines>...
      )
  sqlalchemy.exc.PendingRollbackError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive
  [SQL: UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?]
  [parameters: (-8, 2)]
  (Background on this error at: https://sqlalche.me/e/20/gkpj) (Background on this error at: https://sqlalche.me/e/20/7s2a)
  [('chocolate chip', 3), ('dark chocolate chip', 1)]