Skip to main content

sqlalchemy-pydantic-mapper

sqlalchemy-pydantic-mapper simplifies converting SQLAlchemy instances (subclasses of sqlalchemy.orm.DeclarativeBase) into Pydantic models (pydantic.BaseModel).

It supports:

  • registering custom synchronous and asynchronous mappers;
  • registration via decorator or by passing func= directly;
  • automatic mapping via Pydantic if the model has model_config = ConfigDict(from_attributes=True);
  • map(...) — an async method returning the target Pydantic model instance (must await it);
  • map_each(...) — an async method returning the sequence of target Pydantic model instances, calls target func for every ORM model (must await it);
  • map_bulk(...) — an async method returning a list of mapped Pydantic model instances from a sequence of SQLAlchemy objects(must await it).

Usage Examples (Full Code Snippets)

  1. Simple registration via func= and checking _mappers:
from sqlalchemy_pydantic_mapper import ObjectMapper


def mapper(db: UserDB) -> UserSchema:
    return UserSchema(id=db.id, name=db.name)


ObjectMapper.register(UserDB, UserSchema, func=mapper)
assert ObjectMapper._mappers_single[UserDB][UserSchema] is mapper
  1. Registration via decorator:
@ObjectMapper.register(UserDB, UserSchema)
def mapper2(db: UserDB) -> UserSchema:
    return UserSchema(id=db.id, name=db.name)
  1. Async mapper (registration + usage):
@ObjectMapper.register(UserDB, UserSchema)
async def async_mapper(db: UserDB) -> UserSchema:
    # e.g., async call or await something
    return UserSchema(id=db.id, name=db.name.upper())

import asyncio

async def main():
    user = UserDB()
    user.id = 1
    user.name = "alice"
    res = await ObjectMapper.map(user, UserSchema)
    print(res)  # UserSchema(id=1, name='ALICE')

asyncio.run(main())
  1. Auto-mapping via Pydantic (from_attributes=True):
class UserSchema(BaseModel):
    model_config = ConfigDict(from_attributes=True)
    id: int
    name: str

If no custom mapper is registered for the from_class -> to_class pair, ObjectMapper.map(instance, UserSchema) automatically calls:

UserSchema.model_validate(instance, from_attributes=True)
  1. Example in a test (synthetic):
async def test_auto_mapping():
    stud = StudentDB()
    stud.id = 2
    stud.name = "Bob"

    result = await ObjectMapper.map(stud, UserSchema)
    assert result.id == 2
    assert result.name == "Bob"
  1. Example for manual mapping (different names / logic):
def stud_to_studschema(db: StudentDB) -> StudSchema:
    return StudSchema(id=db.id, name=db.name)

ObjectMapper.register(StudentDB, StudSchema, func=stud_to_studschema)

  1. Mapping multiple objects with map_many
users = [UserDB(id=1, name="Alice"), UserDB(id=2, name="Bob")]


# Synchronous mapper
def mapper(db: UserDB) -> UserSchema:
    return UserSchema(id=db.id, name=db.name)


ObjectMapper.register(UserDB, UserSchema, func=mapper)

import asyncio


async def main():
    results = await ObjectMapper.map_each(users, UserSchema)
    print(results)
    # [UserSchema(id=1, name='Alice'), UserSchema(id=2, name='Bob')]


asyncio.run(main())

  1. Passing additional arguments to a mapper function
def mapper_with_prefix(db: UserDB, prefix: str) -> UserSchema:
    return UserSchema(id=db.id, name=f"{prefix}{db.name}")

# Register mapper with a closure to pass extra arguments
ObjectMapper.register(UserDB, UserSchema, func=lambda db: mapper_with_prefix(db, prefix="Mr. "))

user = UserDB(id=1, name="Alice")
import asyncio

async def main():
    result = await ObjectMapper.map(user, UserSchema)
    print(result)  # UserSchema(id=1, name='Mr. Alice')

asyncio.run(main())

  1. Re-registering a mapper (overwriting)
# Original mapper
ObjectMapper.register(UserDB, UserSchema, func=lambda db: UserSchema(id=db.id, name=db.name))

# Re-register with a new logic
ObjectMapper.register(UserDB, UserSchema, 
                      func=lambda db: UserSchema(id=db.id, name=db.name.upper()),
                      override_existing=True)

user = UserDB(id=2, name="Bob")

import asyncio
async def main():
    result = await ObjectMapper.map(user, UserSchema)
    print(result)  # UserSchema(id=2, name='BOB')

asyncio.run(main())

  1. Async mapper with map_each
async def async_mapper(db: UserDB) -> UserSchema:
    import asyncio
    await asyncio.sleep(0.01)
    return UserSchema(id=db.id, name=db.name.upper())


ObjectMapper.register(UserDB, UserSchema, func=async_mapper)

users = [UserDB(id=1, name="Alice"), UserDB(id=2, name="Bob")]


async def main():
    results = await ObjectMapper.map_each(users, UserSchema)
    print(results)
    # [UserSchema(id=1, name='ALICE'), UserSchema(id=2, name='BOB')]


asyncio.run(main())
  1. Async mapper with map_bulk - True bulk operation under all the sequence at once
async def async_mapper(dbs: Sequence[UserDB]) -> Sequence[UserSchema]:
    import asyncio
    await asyncio.sleep(0.01)
    return [UserSchema(id=db.id, name=db.name.upper()) for db in dbs]


ObjectMapper.register_bulk(UserDB, UserSchema, func=async_mapper)

users = [UserDB(id=1, name="Alice"), UserDB(id=2, name="Bob")]


async def main():
    results = await ObjectMapper.map_bulk(users, UserSchema)
    print(results)
    # [UserSchema(id=1, name='ALICE'), UserSchema(id=2, name='BOB')]


asyncio.run(main())

Errors and Behavior on Incorrect Usage

  • TypeError if from_ does not inherit DeclarativeBase:
class NotABase: pass
ObjectMapper.register(NotABase, UserSchema)  # -> TypeError
  • TypeError if to_ does not inherit BaseModel:
class NotABaseModel: pass
ObjectMapper.register(UserDB, NotABaseModel)  # -> TypeError
  • ValueError if func is missing and to_ does not have model_config = ConfigDict(from_attributes=True):
class BadSchema(BaseModel):
    id: int
    name: str

ObjectMapper.register(UserDB, BadSchema)  # -> ValueError

Metadata

Release files for sqlalchemy-pydantic-mapper 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sqlalchemy-pydantic-mapper 0.4.0
File Size Uploaded Provenance
sqlalchemy_pydantic_mapper-0.4.0.tar.gz 11.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sqlalchemy-pydantic-mapper 0.4.0
File Interpreter ABI Platform
sqlalchemy_pydantic_mapper-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 19.2 kB

Release files / sqlalchemy_pydantic_mapper-0.4.0.tar.gz

Download URL sqlalchemy_pydantic_mapper-0.4.0.tar.gz
Size 11.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e08498033951115d551d83bd3a3ade3afded576538b12a5cc964095fe48859ef
BLAKE2b-256 checksum
How to use checksums
e4727bd0f29e9681133ce7b8988a0c83ad33fd53fda5b5dab5c4d9dbfedc1dab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2025.

Transparency log

Release files / sqlalchemy_pydantic_mapper-0.4.0-py3-none-any.whl

Download URL sqlalchemy_pydantic_mapper-0.4.0-py3-none-any.whl
Size 8.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87ab127959804b2c74e79d84a32a520f81ad03fe0718378768488da7bb73bc10
BLAKE2b-256 checksum
How to use checksums
059fb2caddc18cba791a8a5af1932a1eecdb6ff3207d570141cd59a67ececdf3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2025.

Transparency log

Security for sqlalchemy-pydantic-mapper 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for sqlalchemy_pydantic_mapper-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2025.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page