{"id":16853643,"url":"https://github.com/alex-oleshkevich/starlette-sqlalchemy","last_synced_at":"2026-04-12T16:02:34.824Z","repository":{"id":255438181,"uuid":"850712097","full_name":"alex-oleshkevich/starlette-sqlalchemy","owner":"alex-oleshkevich","description":"SQLAlchemy integration with Starlette.","archived":false,"fork":false,"pushed_at":"2024-10-15T19:18:36.000Z","size":55,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-14T20:39:06.053Z","etag":null,"topics":["database","sqlalchemy","starlette"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alex-oleshkevich.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-01T15:11:22.000Z","updated_at":"2024-11-21T08:52:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"88e9dc01-6f7e-4aec-b538-a0238b3164dc","html_url":"https://github.com/alex-oleshkevich/starlette-sqlalchemy","commit_stats":null,"previous_names":["alex-oleshkevich/starlette_sqlalchemy"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-sqlalchemy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-sqlalchemy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-sqlalchemy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-sqlalchemy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alex-oleshkevich","download_url":"https://codeload.github.com/alex-oleshkevich/starlette-sqlalchemy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244199823,"owners_count":20414734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","sqlalchemy","starlette"],"created_at":"2024-10-13T13:52:33.128Z","updated_at":"2025-10-18T20:56:57.324Z","avatar_url":"https://github.com/alex-oleshkevich.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Starlette-SQLAlchemy\n\nSQLAlchemy integration.\n\n![PyPI](https://img.shields.io/pypi/v/starlette_sqlalchemy)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/alex-oleshkevich/starlette_sqlalchemy/Lint)\n![GitHub](https://img.shields.io/github/license/alex-oleshkevich/starlette_sqlalchemy)\n![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/pypi/starlette_sqlalchemy)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/starlette_sqlalchemy)\n![GitHub Release Date](https://img.shields.io/github/release-date/alex-oleshkevich/starlette_sqlalchemy)\n\n## Installation\n\nInstall `starlette_sqlalchemy` using PIP or poetry:\n\n```bash\npip install starlette_sqlalchemy\n# or\npoetry add starlette_sqlalchemy\n```\n\n## Features\n\n- **Vanilla SQLAlchemy** - no wrappers, use plain SQLAlchemy as it is intended\n- **Query helper** - removes boilerplate code when querying the database\n- **Pagination** - automatically paginate SQLAlchemy queries\n- **Session middleware** - create and inject SQLAlchemy session into request state\n- **Model repository** - much like Django's ModelManager, encapsulates model queries in a single place\n- **Repository filters** - reusable and composable filters for model repositories to share complex queries\n\n## Usage\n\n### Query helper\n\nQuery helper reduces amount of boilerplate code for SQLAlchemy queries.\n\n```python\nimport sqlalchemy as sa\n\nfrom starlette_sqlalchemy import query\n\n\nclass User: ...  # SQLAlchemy model\n\n\nstmt = sa.select(User)\n\n\none_model = await query.one(stmt)\n\n# fetch all models\nmany_models = await query.all(stmt)\n\n# fetch model or return None if not found\nnullable_model = await query.one_or_none(stmt)\n\n# fetch model or raise exception if not found\nmodel = await query.one_or_raise(stmt, ValueError(\"Model not found\"))\n\n# fetch model or return default value if not found\nmodel = await query.one_or_default(stmt, User())\n\n# test if model exists\nexists = await query.exists(stmt)\n\n# count models\ncount = await query.count(stmt)\n\n# generate choices for select field (wtforms, etc)\nchoices = await query.choices(stmt, 'id', 'name')\n```\n\n### Pagination\n\nThe library includces a helper for pagination.\n\n```python\nimport sqlalchemy as sa\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom starlette_sqlalchemy import PageNumberPaginator\n\n\nclass User: ...  # SQLAlchemy model\n\n\ndbsession: AsyncSession = ...\n\nstmt = sa.select(User)\npaginator = PageNumberPaginator(dbsession)\npage = await paginator.paginate(stmt, page=1, per_page=10)\nfor page_number in page.items:\n    print(page_number)\n\n```\n\n### Session middleware\n\nSession middleware automatically injects SQLAlchemy session into request state.\n\n```python\nfrom sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession, create_async_engine\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\n\nfrom starlette_sqlalchemy import DbSessionMiddleware\n\nasync_engine = create_async_engine(\"sqlite+aiosqlite:///db.sqlite\")\nsession_factory = async_sessionmaker(async_engine, class_=AsyncSession)\n\napp = Starlette(\n    middleware=[\n        Middleware(DbSessionMiddleware, session_factory=session_factory),\n    ]\n)\n\n\nasync def view(request):\n    dbsession: AsyncSession = request.state.dbsession\n    # do something with dbsession\n```\n\n\n### Model repository\n\nModel repository is a high-level abstraction for working with models.\nThe purpose of the repository is to encapsulate the logic for fetching and storing models.\nIt best shines when used in larger projects where same model may be used in multiple context\nlike admin panel, public-facing API, etc.\n\n```python\nimport sqlalchemy as sa\n\nfrom starlette_sqlalchemy import Repo\n\n\nclass User:\n    is_active: bool  # column\n\n\nclass APIUserRepo(Repo[User]):\n    \"\"\"Returns only active users\"\"\"\n    model_class = User\n    base_query = sa.select(User).where(User.is_active == True)\n\n\nclass AdminUserRepo(Repo[User]):\n    \"\"\"For admin panel, ignore active status and return all models\"\"\"\n    model_class = User\n    base_query = sa.select(User)\n\n\ndbsession: AssertionError = ...\napi_user_repo = APIUserRepo(dbsession)\nadmin_user_repo = AdminUserRepo(dbsession)\n\napi_user_repo.all()  # returns  only active users\nadmin_user_repo.all()  # returns all users\n```\n\nFeel free to extend the repo with custom methods.\n\n\n### Repository filters\n\nRepository filters are composable and reusable SQLAchemy expressions.\nAlso, you can pack complex logic into a single filter.\n\nThis patterns prevents code duplication, makes the codebase more maintainable,\nand reduces amount of silly bugs when you forget to filter out some data in some other place.\n\n```python\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom starlette_sqlalchemy import Repo, RepoFilter\n\n\nclass User:\n    email: str  # column\n\n\nclass UserRepo(Repo[User]):\n    model_class = User\n\n\nclass ByEmailFilter(RepoFilter[User]):\n    def __init__(self, email):\n        self.email = email\n\n    def apply(self, stmt):\n        return stmt.where(User.email == self.email)\n\n\ndbsession: AsyncSession = ...\nrepo = UserRepo(dbsession)\nusers = await repo.all(ByEmailFilter('root@localhost'))\n```\n\n#### Composing filters\n\nFilters can be composed together to create complex queries.\nThe underlying statements will be merged together using `AND` operator.\n\n```python\nimport datetime\n\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom starlette_sqlalchemy import Repo, RepoFilter\n\n\nclass User:\n    is_active: bool  # column\n    registered_at: datetime  # column\n\n\nclass UserRepo(Repo[User]):\n    model_class = User\n\n\nclass OnlyIsActive(RepoFilter[User]):\n    def apply(self, stmt):\n        return stmt.where(User.is_active == True)\n\n\nclass ByRegistrationDate(RepoFilter[User]):\n    def __init__(self, date):\n        self.date = date\n\n    def apply(self, stmt):\n        return stmt.where(User.registered_at \u003e= self.date)\n\n\ndbsession: AsyncSession = ...\nrepo = UserRepo(dbsession)\n\nfilter_ = OnlyIsActive() \u0026 ByRegistrationDate('2022-01-01')\nusers = await repo.all(filter_)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oleshkevich%2Fstarlette-sqlalchemy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falex-oleshkevich%2Fstarlette-sqlalchemy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oleshkevich%2Fstarlette-sqlalchemy/lists"}