Show HN:Advanced-Alchemy——一个与框架无关的SQLAlchemy库
Show HN: Advanced-Alchemy – A framework agnostic library for SQLAlchemy

原始链接: https://github.com/litestar-org/advanced-alchemy

Advanced Alchemy是一个强大的SQLAlchemy伴生库,旨在简化和优化Python应用程序中的数据库交互。它提供同步和异步存储库,用于简化CRUD操作,包括高度优化的批量操作。它与Litestar、FastAPI和Flask等主要Web框架无缝集成,提供便捷的扩展。主要功能包括自动生成的Alembic配置、带有审计列和主键的实用程序基类,以及具有统一存储后端支持的多功能文件对象数据类型。此外,它还具有针对Oracle的优化JSON类型、对UUID6/7和Nano ID的内置支持以及对各种数据库后端的全面支持。该库还提供服务类,使使用存储库更容易。无论您是构建Litestar应用程序还是使用Flask或FastAPI,Advanced Alchemy都为您提供强大的数据持久化解决方案。

Litestar项目的新SQLAlchemy库Advanced-Alchemy旨在简化数据库交互,尤其是在Web应用上下文之外。Hacker News的讨论显示了褒贬不一的反应。一些人赞赏其可重用性和Python中不容易获得的功能。用户发现它在Litestar生态系统中非常有价值,可以简化对正确配置的服务和资源库的操作。一个关键优势是其直观的的数据检索语法。 然而,关于文档方面存在一些担忧,用户建议在前面提供更多代码示例。一些人将其与SQLModel进行不利比较,后者被认为更容易上手,尤其是在CRUD应用中。批评还针对FastAPI等“易于使用”的生态系统趋势,认为它们可能过于武断。一些人认为,从长远来看,使用单独模型(SQLAlchemy和Pydantic)分层架构更稳健,并且像SQLModel这样的库会导致DRY反模式。Msgspec被推荐为替代的序列化库,尽管它缺乏验证功能。

原文

Litestar Logo - Light Litestar Logo - Dark

Check out the project documentation 📚 for more information.

A carefully crafted, thoroughly tested, optimized companion library for SQLAlchemy, offering:

  • Sync and async repositories, featuring common CRUD and highly optimized bulk operations
  • Integration with major web frameworks including Litestar, Starlette, FastAPI, Sanic
  • Custom-built alembic configuration and CLI with optional framework integration
  • Utility base classes with audit columns, primary keys and utility functions
  • Built in File Object data type for storing objects:
    • Unified interface for various storage backends (fsspec and obstore)
    • Optional lifecycle event hooks integrated with SQLAlchemy's event system to automatically save and delete files as records are inserted, updated, or deleted.
  • Optimized JSON types including a custom JSON type for Oracle
  • Integrated support for UUID6 and UUID7 using uuid-utils (install with the uuid extra)
  • Integrated support for Nano ID using fastnanoid (install with the nanoid extra)
  • Pre-configured base classes with audit columns UUID or Big Integer primary keys and a sentinel column.
  • Synchronous and asynchronous repositories featuring:
    • Common CRUD operations for SQLAlchemy models
    • Bulk inserts, updates, upserts, and deletes with dialect-specific enhancements
    • Integrated counts, pagination, sorting, filtering with LIKE, IN, and dates before and/or after.
  • Tested support for multiple database backends including:
  • ...and much more
pip install advanced-alchemy

Advanced Alchemy includes a set of asynchronous and synchronous repository classes for easy CRUD operations on your SQLAlchemy models.

Click to expand the example
from advanced_alchemy import base, repository, config
from sqlalchemy import create_engine
from sqlalchemy.orm import Mapped, sessionmaker


class User(base.UUIDBase):
    # you can optionally override the generated table name by manually setting it.
    __tablename__ = "user_account"  # type: ignore[assignment]
    email: Mapped[str]
    name: Mapped[str]


class UserRepository(repository.SQLAlchemySyncRepository[User]):
    """User repository."""

    model_type = User


db = config.SQLAlchemySyncConfig(connection_string="duckdb:///:memory:", session_config=config.SyncSessionConfig(expire_on_commit=False))

# Initializes the database.
with db.get_engine().begin() as conn:
    User.metadata.create_all(conn)

with db.get_session() as db_session:
    repo = UserRepository(session=db_session)
    # 1) Create multiple users with `add_many`
    bulk_users = [
        {"email": '[email protected]', 'name': 'Cody'},
        {"email": '[email protected]', 'name': 'Janek'},
        {"email": '[email protected]', 'name': 'Peter'},
        {"email": '[email protected]', 'name': 'Jacob'}
    ]
    objs = repo.add_many([User(**raw_user) for raw_user in bulk_users])
    db_session.commit()
    print(f"Created {len(objs)} new objects.")

    # 2) Select paginated data and total row count.  Pass additional filters as kwargs
    created_objs, total_objs = repo.list_and_count(LimitOffset(limit=10, offset=0), name="Cody")
    print(f"Selected {len(created_objs)} records out of a total of {total_objs}.")

    # 3) Let's remove the batch of records selected.
    deleted_objs = repo.delete_many([new_obj.id for new_obj in created_objs])
    print(f"Removed {len(deleted_objs)} records out of a total of {total_objs}.")

    # 4) Let's count the remaining rows
    remaining_count = repo.count()
    print(f"Found {remaining_count} remaining records after delete.")

For a full standalone example, see the sample here

Advanced Alchemy includes an additional service class to make working with a repository easier. This class is designed to accept data as a dictionary or SQLAlchemy model, and it will handle the type conversions for you.

Here's the same example from above but using a service to create the data:
from advanced_alchemy import base, repository, filters, service, config
from sqlalchemy import create_engine
from sqlalchemy.orm import Mapped, sessionmaker


class User(base.UUIDBase):
    # you can optionally override the generated table name by manually setting it.
    __tablename__ = "user_account"  # type: ignore[assignment]
    email: Mapped[str]
    name: Mapped[str]

class UserService(service.SQLAlchemySyncRepositoryService[User]):
    """User repository."""
    class Repo(repository.SQLAlchemySyncRepository[User]):
        """User repository."""

        model_type = User

    repository_type = Repo

db = config.SQLAlchemySyncConfig(connection_string="duckdb:///:memory:", session_config=config.SyncSessionConfig(expire_on_commit=False))

# Initializes the database.
with db.get_engine().begin() as conn:
    User.metadata.create_all(conn)

with db.get_session() as db_session:
    service = UserService(session=db_session)
    # 1) Create multiple users with `add_many`
    objs = service.create_many([
        {"email": '[email protected]', 'name': 'Cody'},
        {"email": '[email protected]', 'name': 'Janek'},
        {"email": '[email protected]', 'name': 'Peter'},
        {"email": '[email protected]', 'name': 'Jacob'}
    ])
    print(objs)
    print(f"Created {len(objs)} new objects.")

    # 2) Select paginated data and total row count.  Pass additional filters as kwargs
    created_objs, total_objs = service.list_and_count(LimitOffset(limit=10, offset=0), name="Cody")
    print(f"Selected {len(created_objs)} records out of a total of {total_objs}.")

    # 3) Let's remove the batch of records selected.
    deleted_objs = service.delete_many([new_obj.id for new_obj in created_objs])
    print(f"Removed {len(deleted_objs)} records out of a total of {total_objs}.")

    # 4) Let's count the remaining rows
    remaining_count = service.count()
    print(f"Found {remaining_count} remaining records after delete.")

Advanced Alchemy works with nearly all Python web frameworks. Several helpers for popular libraries are included, and additional PRs to support others are welcomed.

Advanced Alchemy is the official SQLAlchemy integration for Litestar.

In addition to installing with pip install advanced-alchemy, it can also be installed as a Litestar extra with pip install litestar[sqlalchemy].

Litestar Example
from litestar import Litestar
from litestar.plugins.sqlalchemy import SQLAlchemyPlugin, SQLAlchemyAsyncConfig
# alternately...
# from advanced_alchemy.extensions.litestar import SQLAlchemyAsyncConfig, SQLAlchemyPlugin

alchemy = SQLAlchemyPlugin(
  config=SQLAlchemyAsyncConfig(connection_string="sqlite+aiosqlite:///test.sqlite"),
)
app = Litestar(plugins=[alchemy])

For a full Litestar example, check here

Flask Example
from flask import Flask
from advanced_alchemy.extensions.flask import AdvancedAlchemy, SQLAlchemySyncConfig

app = Flask(__name__)
alchemy = AdvancedAlchemy(
    config=SQLAlchemySyncConfig(connection_string="duckdb:///:memory:"), app=app,
)

For a full Flask example, see here

FastAPI Example
from advanced_alchemy.extensions.fastapi import AdvancedAlchemy, SQLAlchemyAsyncConfig
from fastapi import FastAPI

app = FastAPI()
alchemy = AdvancedAlchemy(
    config=SQLAlchemyAsyncConfig(connection_string="sqlite+aiosqlite:///test.sqlite"), app=app,
)

For a full FastAPI example with optional CLI integration, see here

Pre-built Example Apps
from advanced_alchemy.extensions.starlette import AdvancedAlchemy, SQLAlchemyAsyncConfig
from starlette.applications import Starlette

app = Starlette()
alchemy = AdvancedAlchemy(
    config=SQLAlchemyAsyncConfig(connection_string="sqlite+aiosqlite:///test.sqlite"), app=app,
)
Pre-built Example Apps
from sanic import Sanic
from sanic_ext import Extend

from advanced_alchemy.extensions.sanic import AdvancedAlchemy, SQLAlchemyAsyncConfig

app = Sanic("AlchemySanicApp")
alchemy = AdvancedAlchemy(
    sqlalchemy_config=SQLAlchemyAsyncConfig(connection_string="sqlite+aiosqlite:///test.sqlite"),
)
Extend.register(alchemy)

All Litestar Organization projects will always be a community-centered, available for contributions of any size.

Before contributing, please review the contribution guide.

If you have any questions, reach out to us on Discord, our org-wide GitHub discussions page, or the project-specific GitHub discussions page.


Litestar Logo - Light
An official Litestar Organization Project

联系我们 contact @ memedata.com