Repository¶
Repository wraps common CRUD operations for a model + session pair so handlers don't repeat select() boilerplate.
from shakti.orm import Repository
users = Repository(User, session)
user = await users.get(1) # -> User | None
user = await users.get_or_404(1) # -> User, or raises HTTPException(404)
all_ = await users.all() # -> list[User]
found = await users.filter(role="admin") # -> list[User], AND-ed equality filters
one = await users.first(email="a@b.com")# -> User | None
new = await users.create(email="a@b.com", username="a")
await users.update(user, username="renamed")
await users.delete(user)
count = await users.count(role="admin")
exists = await users.exists(email="a@b.com")
create and update flush and refresh the object immediately, so the returned instance has its generated id (and any server-side defaults) populated — you don't need a manual session.refresh() afterward. create/update/delete still rely on the surrounding session's commit boundary (see Database) — nothing is committed until the session itself commits.
In a handler¶
Typical pattern: a small dependency that builds a Repository bound to the request's session, then inject it:
from shakti import Depends
from shakti.orm import Database, Repository
from sqlalchemy.ext.asyncio import AsyncSession
def _posts(session: AsyncSession = Depends(db.get_session)) -> Repository[Post]:
return Repository(Post, session)
@app.get("/posts/{id:int}")
async def get_post(id: int, repo: Repository = Depends(_posts)) -> dict:
post = await repo.get_or_404(id)
return post.to_dict()
@app.post("/posts")
async def create_post(body: dict, repo: Repository = Depends(_posts)) -> dict:
post = await repo.create(**body)
return post.to_dict()
This is exactly the pattern shakti generate crud / shakti generate api scaffold for you automatically — see Code Generation.
filter semantics¶
filter(**kwargs) builds a WHERE clause of AND-ed equality comparisons (column == value for each kwarg) — it doesn't support >, LIKE, OR, joins, or anything beyond exact-match filtering. For anything more expressive, drop down to SQLAlchemy's select() directly against repo.session (or just use a session without Repository at all — it's a convenience layer, not a required abstraction).
get_or_404¶
Raises HTTPException(404, f"{Model.__name__} not found") when the primary key doesn't exist — handy for the common "fetch or 404" pattern without an explicit if obj is None: raise ... in every handler.