UP | HOME

Essential SQLAlchemy (v1/v2)

Table of Contents

1. Chapter 1: Schema and Types

1.1. 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)),
)
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"),
)
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)),
)
from sqlalchemy import create_engine

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

1.2. 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)

2. Chapter 2: Working with Data via SQLAlchemy Core

2.1. 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)

2.1.1. 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

2.1.2. Querying Data

from sqlalchemy.sql import select

s = select(cookies)
# ResultProxy
rp = connection.execute(s)
rp.fetchall()
s = cookies.select()
rp = connection.execute(s)
results = rp.fetchall()
results
  1. 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]
    )
    
    rp = connection.execute(s)
    for record in rp:
        print(record.cookie_name)
    
  2. 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)
    
  3. 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))
    
    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()
    
  4. 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])
    
  5. Functions and Labels
    from sqlalchemy.sql import func
    
    s = select(func.sum(cookies.c.quantity))
    rp = connection.execute(s)
    print(rp.scalar())
    
    s = select(func.count(cookies.c.cookie_name))
    rp = connection.execute(s)
    record = rp.first()
    print(record._fields)
    print(record.count_1)
    
    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)
    
  6. 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()
    ]
    
    s = select(cookies).where(cookies.c.cookie_name.like("%chocolate%"))
    rp = connection.execute(s)
    for record in rp:
        print(record.cookie_name)
    
    s = select(cookies.c.cookie_name, "SKU-" + cookies.c.cookie_sku)
    [row for row in connection.execute(s)]
    
    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)]
    
  7. 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)]
    
    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)]
    

2.1.3. 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
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()
]

2.1.4. Deleting Data

from sqlalchemy import delete

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

2.1.5. 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]
s = select(orders)
rp = connection.execute(s)
[tuple(f for f in rp.keys())] + [row for row in rp]
s = select(users)
rp = connection.execute(s)
[tuple(f for f in rp.keys())] + [row for row in rp]
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]

2.2. 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)

2.2.1. 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)
import json

json.dumps(ins.compile().params, indent=2)
result = connection.execute(ins)
connection.commit()
result.inserted_primary_key
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
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
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.2.2. Querying Data

from sqlalchemy import select

s = select(cookies)
result = connection.execute(s)
result.all()
s = select(cookies)
results = connection.execute(s).all()
results
  1. Result
    first_row = results[0]
    (
        first_row[1],
        first_row.cookie_name,
        first_row._mapping[cookies.c.cookie_name]
    )
    
    rp = connection.execute(select(cookies))
    for record in rp:
        print(record.cookie_name)
    
  2. 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)
    
  3. 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))
    
    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()
    
  4. 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])
    
  5. Functions and Labels
    from sqlalchemy import func
    
    s = select(func.sum(cookies.c.quantity))
    rp = connection.execute(s)
    print(rp.scalar())
    
    s = select(func.count(cookies.c.cookie_name))
    rp = connection.execute(s)
    record = rp.first()
    print(record._fields)
    print(record.count_1)
    
    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)
    
  6. 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()
    ]
    
    s = select(cookies).where(cookies.c.cookie_name.like("%chocolate%"))
    rp = connection.execute(s)
    for record in rp:
        print(record.cookie_name)
    
    s = select(cookies.c.cookie_name, "SKU-" + cookies.c.cookie_sku)
    [row for row in connection.execute(s)]
    
    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)]
    
  7. 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)]
    
    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)]
    

2.2.3. 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
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()
]

2.2.4. 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
s = select(cookies).where(cookies.c.cookie_name == "dark chocolate chip")
result = connection.execute(s).all()
len(result)

2.2.5. 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]
s = select(orders)
rp = connection.execute(s)
[tuple(f for f in rp.keys())] + [row for row in rp]
s = select(users)
rp = connection.execute(s)
[tuple(f for f in rp.keys())] + [row for row in rp]
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]

3. Chapter 3: Exceptions and Transactions

3.1. 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)

3.1.1. 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)

3.1.2. 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))

3.1.3. 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())
try:
    ship_it(2)
except IntegrityError:
    logging.exception("")
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()

3.2. 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("")

4. 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()

4.0.1. 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)

4.0.2. 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

5. Chapter 5: Reflection

5.1. 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()
from sqlalchemy import select

with engine.begin() as connection:
    s = select(artist).limit(10)
    result = connection.execute(s).fetchall()
result
album = Table('Album', metadata, autoload_with=engine)
res = metadata.tables['Album']
repr(res)
album.foreign_keys

5.1.1. Reflecting a Whole Database

metadata.reflect(bind=engine)
list(metadata.tables.keys())

6. Chapter 6: Defining Schema with SQLAlchemy ORM

6.1. 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,
)
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,
)

6.1.1. 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"),
    )

6.1.2. 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,
)

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,
)
Order.display()
from sqlalchemy import create_engine
engine = create_engine("sqlite:///:memory:")

Base.metadata.create_all(engine)

6.2. 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,
)

6.2.1. 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))

6.2.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)

7. Chapter 7: Working with Data via SQLAlchemy ORM

7.1. SQLAlchemy v1

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine("sqlite:///:memory:")
Session = sessionmaker(bind=engine)
session = Session()
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()
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()
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()
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()

Base.metadata.create_all(engine)

7.1.1. 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

7.1.2. 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",
)
]
  1. Controlling the Columns in the Query
    session.query(Cookie.cookie_name, Cookie.quantity).first()
    
    chocolate chip 12
  2. 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
  3. 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
  4. 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
  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
  6. 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')
  7. 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

7.1.3. 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

7.1.4. 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

7.1.5. 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()

7.1.6. 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)

7.1.7. 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

7.1.8. 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)

7.1.9. 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",
)

7.2. 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)

8. Chapter 8: Understanding the Session and Exceptions

8.1. SQLAlchemy v1

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

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

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()
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()
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()
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()

Base.metadata.create_all(engine)

8.2. SQLAlchemy v2

Created: 2026-09-21 Пн 12:00

Validate