Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ jobs:
tests/test_docs_examples.py \
tests/test_docstring_examples.py \
tests/test_docs_migrations.py \
tests/test_docs_sql.py \
tests/test_docs_testing_fixtures.py \
tests/test_docs_checks.py \
tests/test_docs_lint.py \
Expand Down
11 changes: 11 additions & 0 deletions docs/examples/aggregations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

import asyncio
from collections import defaultdict
from decimal import Decimal
from typing import Annotated

Expand Down Expand Up @@ -237,6 +238,16 @@ async def main() -> None:
assert row.avg_price == Decimal("3")
# --8<-- [end:decimal-contract]

# --8<-- [start:partition]
# Whole instances bucketed by a key: partition a full query in Python.
by_account: dict[int | None, list[Transaction]] = defaultdict(list)
for transaction in await Transaction.select().all():
by_account[transaction.account_id].append(transaction)

sizes = {key: len(rows) for key, rows in by_account.items()}
assert sizes == {1: 2, 2: 1, None: 1}
# --8<-- [end:partition]

# --8<-- [start:error-family]
# sum() over a string column has no portable meaning.
try:
Expand Down
29 changes: 28 additions & 1 deletion docs/examples/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ class Customer(Model):
# --8<-- [end:models]


# --8<-- [start:conversation]
class Conversation(Model):
id: int | None = Field(default=None, primary_key=True)
turns: dict = Field(default_factory=dict)


# --8<-- [end:conversation]


async def main() -> None:
# --8<-- [start:session]
await connect("sqlite::memory:", auto_migrate=True)
Expand Down Expand Up @@ -68,7 +77,7 @@ async def main() -> None:
# --8<-- [start:save-insert-update]
frank = Customer(email="frank@example.com", name="Frank")
await frank.save() # never persisted: INSERT
assert frank.id == 6
assert frank.id is not None # the database assigned it

frank.plan = "pro"
await frank.save() # persisted: UPDATE by primary key
Expand Down Expand Up @@ -133,6 +142,24 @@ async def main() -> None:
assert alice.views == 1 and alice.updated_at is not None
# --8<-- [end:recipe-update]

# .merge() exists on PostgreSQL only; the rendered statement says
# which database this session runs on.
on_postgres = (await Customer.select().count.sql()).dialect == "postgres"
if on_postgres:
# --8<-- [start:merge]
chat = await Conversation.create()
await Conversation.where(
lambda conversation: conversation.id == chat.id
).update(
lambda conversation: {
"turns": conversation.turns.merge({"r1": {"role": "user"}})
}
)

await chat.refresh()
assert chat.turns == {"r1": {"role": "user"}}
# --8<-- [end:merge]

# --8<-- [start:mutation-guard]
trial = Customer.where(lambda customer: customer.plan == "trial")
try:
Expand Down
9 changes: 9 additions & 0 deletions docs/examples/mutations_annotated.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class Customer(Model):
# --8<-- [end:models]


# --8<-- [start:conversation]
class Conversation(Model):
id: Annotated[int | None, Field(primary_key=True)] = None
turns: Annotated[dict, Field(default_factory=dict)]


# --8<-- [end:conversation]


async def main() -> None:
await connect("sqlite::memory:", auto_migrate=True)

Expand Down
3 changes: 2 additions & 1 deletion docs/examples/populated_relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ async def main() -> None:
assert from_checking.account.label == "Savings"

# Move the row to another account behind the ORM's back, then fetch it.
await execute('UPDATE "transaction" SET account_id = ? WHERE id = ?', 1, 5)
# ($1 and $2 are placeholders SQLite and PostgreSQL both accept.)
await execute('UPDATE "transaction" SET account_id = $1 WHERE id = $2', 1, 5)
await Transaction.get(5) # refreshes the instance in place

pending = from_checking.account # awaitable again
Expand Down
43 changes: 14 additions & 29 deletions docs/examples/relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,12 @@ async def main() -> None:
if statement.subject == "player":
print(statement.sql)
# --8<-- [end:player-ddl]
assert table_ddl(report, "player") == [
'CREATE TABLE IF NOT EXISTS "player" ( "id" integer NOT NULL PRIMARY KEY '
'AUTOINCREMENT, "mentor_id" integer, "name" varchar NOT NULL, "team_id" '
'integer NOT NULL, CONSTRAINT "fk_player_mentor_id_player" FOREIGN KEY '
'("mentor_id") REFERENCES "player" ("id") ON DELETE SET NULL, CONSTRAINT '
'"fk_player_team_id_team" FOREIGN KEY ("team_id") REFERENCES "team" ("id") '
"ON DELETE CASCADE )",
'CREATE INDEX IF NOT EXISTS "idx_player_team_id" ON "player" ("team_id")',
]
# The page shows these statements whole, on both databases
# (tests/test_docs_sql.py); here, the names each relation gives them.
player_ddl = "\n".join(table_ddl(report, "player"))
assert 'CONSTRAINT "fk_player_mentor_id_player"' in player_ddl
assert 'CONSTRAINT "fk_player_team_id_team"' in player_ddl
assert '"idx_player_team_id" ON "player" ("team_id")' in player_ddl

# --8<-- [start:populated]
players = (
Expand All @@ -134,14 +131,11 @@ async def main() -> None:
assert profile.bio == "Plays in the front row"
assert await corro.profile is None # Corro has no profile
# --8<-- [end:one-to-one]
assert table_ddl(report, "profile") == [
'CREATE TABLE IF NOT EXISTS "profile" ( "bio" varchar NOT NULL, "id" integer '
'NOT NULL PRIMARY KEY AUTOINCREMENT, "player_id" integer NOT NULL, '
'CONSTRAINT "fk_profile_player_id_player" FOREIGN KEY ("player_id") '
'REFERENCES "player" ("id") ON DELETE CASCADE )',
# unique=True gives the shadow column a unique index.
assert (
'CREATE UNIQUE INDEX IF NOT EXISTS "uq_profile_player_id" ON "profile" '
'("player_id")',
]
'("player_id")'
) in table_ddl(report, "profile")

# --8<-- [start:many-to-many]
spring = await Tournament.create(name="Spring Open")
Expand Down Expand Up @@ -177,19 +171,10 @@ async def main() -> None:
if statement.subject == "player_tournaments":
print(statement.sql)
# --8<-- [end:join-table-ddl]
assert table_ddl(report, "player_tournaments") == [
'CREATE TABLE IF NOT EXISTS "player_tournaments" ( "player_id" integer NOT '
'NULL, "tournament_id" integer NOT NULL, CONSTRAINT '
'"fk_player_tournaments_player_id_player" FOREIGN KEY ("player_id") '
'REFERENCES "player" ("id") ON DELETE CASCADE, CONSTRAINT '
'"fk_player_tournaments_tournament_id_tournament" FOREIGN KEY '
'("tournament_id") REFERENCES "tournament" ("id") ON DELETE CASCADE )',
'CREATE INDEX IF NOT EXISTS "idx_player_tournaments_tournament_id_player_id" '
'ON "player_tournaments" ("tournament_id", "player_id")',
"CREATE UNIQUE INDEX IF NOT EXISTS "
'"uq_player_tournaments_player_id_tournament_id" ON "player_tournaments" '
'("player_id", "tournament_id")',
]
link_ddl = "\n".join(table_ddl(report, "player_tournaments"))
assert 'CONSTRAINT "fk_player_tournaments_player_id_player"' in link_ddl
assert '"idx_player_tournaments_tournament_id_player_id"' in link_ddl
assert '"uq_player_tournaments_player_id_tournament_id"' in link_ddl

# --8<-- [start:self-referential]
crabby = await Player.create(name="Crabby", team=rustaceans, mentor=ferris)
Expand Down
Loading
Loading