Initial release: async repository layer over SQLModel
This commit is contained in:
0
examples/__init__.py
Normal file
0
examples/__init__.py
Normal file
52
examples/basic_usage.py
Normal file
52
examples/basic_usage.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import asyncio
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings
|
||||
|
||||
|
||||
class UserTable(BaseTable, table=True):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
email: str = Field(unique=True)
|
||||
|
||||
|
||||
class UserRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[UserTable]:
|
||||
return UserTable
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
repository = UserRepository(settings=settings)
|
||||
|
||||
await repository.create_tables()
|
||||
|
||||
# Create
|
||||
user = await repository.create_item(
|
||||
UserTable(name="Alice", email="alice@example.com"),
|
||||
)
|
||||
print(f"Created: {user.name}, {user.email}")
|
||||
|
||||
# Read all
|
||||
items = [item async for item in repository.get_items()]
|
||||
print(f"All items: {[(item.name, item.email) for item in items]}")
|
||||
|
||||
# Count
|
||||
count = await repository.get_items_count()
|
||||
print(f"Count: {count}")
|
||||
|
||||
# Update
|
||||
updated = [item async for item in repository.update_items(name="Updated")]
|
||||
print(f"Updated: {[(item.name, item.email) for item in updated]}")
|
||||
|
||||
# Delete
|
||||
await repository.delete_items()
|
||||
count = await repository.get_items_count()
|
||||
print(f"Count after delete: {count}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
59
examples/container_usage.py
Normal file
59
examples/container_usage.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import asyncio
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings, RepositoriesContainer
|
||||
|
||||
|
||||
class UserTable(BaseTable, table=True):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
|
||||
|
||||
class OrderTable(BaseTable, table=True):
|
||||
__tablename__ = "orders"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
user_id: int
|
||||
total: float
|
||||
|
||||
|
||||
class UserRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[UserTable]:
|
||||
return UserTable
|
||||
|
||||
|
||||
class OrderRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[OrderTable]:
|
||||
return OrderTable
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
container = RepositoriesContainer(settings=settings)
|
||||
|
||||
user_repo = container.get_repository(UserRepository)
|
||||
order_repo = container.get_repository(OrderRepository)
|
||||
|
||||
await user_repo.create_tables()
|
||||
await order_repo.create_tables()
|
||||
|
||||
# Atomic transaction across two repositories
|
||||
async with container.transaction():
|
||||
user = await user_repo.create_item(UserTable(name="Alice"))
|
||||
await order_repo.create_item(OrderTable(user_id=user.id, total=100.00))
|
||||
await order_repo.create_item(OrderTable(user_id=user.id, total=250.50))
|
||||
|
||||
# Verify results
|
||||
users = [item async for item in user_repo.get_items()]
|
||||
orders = [item async for item in order_repo.get_items()]
|
||||
|
||||
print(f"Users: {[user.name for user in users]}")
|
||||
print(f"Orders: {[(order.user_id, order.total) for order in orders]}")
|
||||
print(f"Orders count: {len(orders)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
56
examples/dto_usage.py
Normal file
56
examples/dto_usage.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import asyncio
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Field
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: int | None = None
|
||||
name: str
|
||||
email: str
|
||||
|
||||
|
||||
class UserTable(BaseTable[User], table=True):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
email: str = Field(unique=True)
|
||||
|
||||
@classmethod
|
||||
def from_item(cls, item: User) -> "UserTable":
|
||||
return cls(id=item.id, name=item.name, email=item.email)
|
||||
|
||||
def to_item(self) -> User:
|
||||
return User(id=self.id, name=self.name, email=self.email)
|
||||
|
||||
|
||||
class UserRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[UserTable]:
|
||||
return UserTable
|
||||
|
||||
def get_dto_type(self) -> type[User]:
|
||||
return User
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
repository = UserRepository(settings=settings)
|
||||
|
||||
await repository.create_tables()
|
||||
|
||||
# Create using DTO
|
||||
user = await repository.create_item(
|
||||
User(name="Alice", email="alice@example.com"),
|
||||
)
|
||||
print(f"Created DTO: {user.model_dump()}")
|
||||
|
||||
# Read all — returned as DTOs
|
||||
items = [item async for item in repository.get_items()]
|
||||
print(f"Items as DTOs: {[item.model_dump() for item in items]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
64
examples/filter_usage.py
Normal file
64
examples/filter_usage.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import asyncio
|
||||
|
||||
from pydantic_filters import BaseFilter, BaseSort, OffsetPagination
|
||||
from sqlmodel import Field
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings
|
||||
|
||||
|
||||
class BookTable(BaseTable, table=True):
|
||||
__tablename__ = "books"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
title: str
|
||||
year: int
|
||||
|
||||
|
||||
class BookFilter(BaseFilter):
|
||||
title: str | None = None
|
||||
year: int | None = None
|
||||
|
||||
|
||||
class BookRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[BookTable]:
|
||||
return BookTable
|
||||
|
||||
def get_filter_type(self) -> type[BookFilter]:
|
||||
return BookFilter
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
repository = BookRepository(settings=settings)
|
||||
|
||||
await repository.create_tables()
|
||||
|
||||
# Seed data
|
||||
for index in range(10):
|
||||
await repository.create_item(
|
||||
BookTable(title=f"Book {index}", year=2020 + index),
|
||||
)
|
||||
|
||||
# Filter by year = 2025
|
||||
year_filter = BookFilter(year=2025)
|
||||
filtered = [item async for item in repository.get_items(filter_=year_filter)]
|
||||
print(f"Year = 2025: {[book.title for book in filtered]}")
|
||||
|
||||
# Filter by exact title
|
||||
title_filter = BookFilter(title="Book 5")
|
||||
filtered = [item async for item in repository.get_items(filter_=title_filter)]
|
||||
print(f"Title = 'Book 5': {[book.title for book in filtered]}")
|
||||
|
||||
# Pagination
|
||||
pagination = OffsetPagination(offset=2, limit=3)
|
||||
page = [item async for item in repository.get_items(pagination=pagination)]
|
||||
print(f"Page (offset=2, limit=3): {[book.title for book in page]}")
|
||||
|
||||
# Sorting descending
|
||||
sort = BaseSort(sort_by="year", sort_by_order="desc")
|
||||
sorted_items = [item async for item in repository.get_items(sort=sort)]
|
||||
print(f"Sorted by year desc: {[book.year for book in sorted_items]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
63
examples/relationships.py
Normal file
63
examples/relationships.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import asyncio
|
||||
|
||||
from sqlalchemy.orm import joinedload
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings, RepositoriesContainer
|
||||
|
||||
|
||||
class AuthorTable(BaseTable, table=True):
|
||||
__tablename__ = "authors"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
books: list["BookTable"] = Relationship(back_populates="author")
|
||||
|
||||
|
||||
class BookTable(BaseTable, table=True):
|
||||
__tablename__ = "books"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
title: str
|
||||
author_id: int = Field(foreign_key="authors.id")
|
||||
author: AuthorTable = Relationship(back_populates="books")
|
||||
|
||||
|
||||
class BookRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[BookTable]:
|
||||
return BookTable
|
||||
|
||||
|
||||
class AuthorRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[AuthorTable]:
|
||||
return AuthorTable
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
container = RepositoriesContainer(settings=settings)
|
||||
|
||||
author_repo = AuthorRepository(container=container)
|
||||
book_repo = BookRepository(container=container)
|
||||
|
||||
await author_repo.create_tables()
|
||||
await book_repo.create_tables()
|
||||
|
||||
# Create author and books
|
||||
author = await author_repo.create_item(AuthorTable(name="Tolkien"))
|
||||
await book_repo.create_item(BookTable(title="The Hobbit", author_id=author.id))
|
||||
await book_repo.create_item(BookTable(title="LOTR", author_id=author.id))
|
||||
|
||||
# Load books with authors using joinedload
|
||||
books = [
|
||||
item
|
||||
async for item in book_repo.get_items(
|
||||
options=[joinedload(BookTable.author)],
|
||||
)
|
||||
]
|
||||
for book in books:
|
||||
print(f"Book: {book.title}, Author: {book.author.name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
44
examples/transactions.py
Normal file
44
examples/transactions.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import asyncio
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from metaorm import BaseRepository, BaseTable, DatabaseSettings
|
||||
|
||||
|
||||
class ProductTable(BaseTable, table=True):
|
||||
__tablename__ = "products"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
price: float
|
||||
|
||||
|
||||
class ProductRepository(BaseRepository):
|
||||
def get_db_table(self) -> type[ProductTable]:
|
||||
return ProductTable
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = DatabaseSettings(dsn="sqlite+aiosqlite:///:memory:")
|
||||
repository = ProductRepository(settings=settings)
|
||||
|
||||
await repository.create_tables()
|
||||
|
||||
# Explicit transaction via repository
|
||||
async with repository.transaction():
|
||||
product1 = await repository.create_item(
|
||||
ProductTable(name="Laptop", price=999.99),
|
||||
)
|
||||
product2 = await repository.create_item(
|
||||
ProductTable(name="Mouse", price=29.99),
|
||||
)
|
||||
print(f"Created in transaction: {product1.name}, {product2.name}")
|
||||
|
||||
# Nested transaction reuses existing session
|
||||
async with repository.transaction(), repository.transaction():
|
||||
items = [item async for item in repository.get_items()]
|
||||
print(f"Items in nested transaction: {len(items)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user