Library / SDK
tiangolo/pydantic-sqlalchemy avatar
tiangolo/pydantic-sqlalchemy

Pydantic-SQLAlchemy for Dynamic Model Conversion

Tools to convert SQLAlchemy models to Pydantic models

1,408 stars74 forksPythonMIT

At a glance

What is it?
Pydantic-SQLAlchemy generates Pydantic models from SQLAlchemy declarative classes, but the README labels it experimental and recommends SQLModel for most projects. Its declared dependencies also restrict it to SQLAlchemy 1.x and Pydantic 1.x.
Who is it for?
Pydantic-SQLAlchemy can serve as a bridge for an existing SQLAlchemy 1.x codebase that needs generated Pydantic 1.x models with limited static typing support. It is a poor fit for a new application, particularly one already committed to SQLAlchemy 2.x or Pydantic 2.x. Verify dependency compatibility first, then check whether dynamic models provide enough autocomplete and inline errors for the development workflow.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tiangolo/pydantic-sqlalchemy1,408 ★ · -2
Star history
Repository2026-10-172026-11-06
tiangolo/pydantic-sqlalchemy1.4k1.4k

-2 Stars in the last 20 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Why Pydantic-SQLAlchemy Exists

Pydantic-SQLAlchemy addresses a narrow conversion problem: a Python project already defines database entities with SQLAlchemy declarative models but needs Pydantic-compatible representations of those same classes. The generated models create a boundary between SQLAlchemy objects used by the persistence layer and dictionary-oriented or serialized data handled through Pydantic. This arrangement can suit code that needs Pydantic validation or serialization without maintaining a separate handwritten model for every SQLAlchemy entity.

Its audience is therefore more specific than developers seeking a general ORM abstraction. The project description identifies its purpose as converting SQLAlchemy models to Pydantic models. The README does not present it as a replacement for SQLAlchemy queries, sessions, relationships, or schema migrations. Those responsibilities remain with SQLAlchemy in the documented example.

That distinction matters. The converter produces Pydantic-facing shapes, but it does not remove the database layer or provide a new query API. A service can continue loading instances through SQLAlchemy and then convert selected objects at an application boundary.

The main compromise concerns editor support. The README states that dynamically generated Pydantic models lack some of the autocompletion and inline errors found in explicitly declared models. Conversion can preserve mapped fields while weakening the static interface available while code is being written. The author labels the project experimental and recommends SQLModel for most uses. That warning should be treated as part of the project's scope, not dismissed as generic caution.

How SQLAlchemy Classes Become Pydantic Models

The documented conversion path has four stages. SQLAlchemy declarative classes define tables, columns, and relationships. The `sqlalchemy_to_pydantic()` function receives one of those classes and returns a new Pydantic model. SQLAlchemy then persists or loads an ORM instance. Finally, `from_orm()` converts that ORM object, and `.dict()` returns a dictionary.

The central conversion is concise:

python
PydanticUser = sqlalchemy_to_pydantic(User)
PydanticAddress = sqlalchemy_to_pydantic(Address)

This produces separate generated classes for the two mapped entities. The converter is therefore model-oriented rather than a one-time conversion of an entire database schema. It is invoked for each SQLAlchemy class that needs a Pydantic representation.

The generated classes primarily represent the fields inferred from the SQLAlchemy model. Relationships require additional application definition in the documented workflow. The README creates a new class named `PydanticUserWithAddresses` after generating `PydanticUser`, then adds a typed `addresses` field to that subclass. This shows that nested ORM relationships are not automatically represented in every generated output class.

The data source remains a SQLAlchemy ORM instance. Calling `PydanticUser.from_orm(user)` expects a loaded `User`, while the relationship-aware subclass expects the related address collection to be accessible. The conversion does not issue queries or replace SQLAlchemy's session behavior.

Building the README's SQLite Entity Example

The README uses an in-memory SQLite database to demonstrate the complete path from declarative models to Pydantic dictionaries. The `sqlite://` URL creates an engine without a persistent database file, and `echo=True` enables SQLAlchemy's SQL logging. The example is self-contained at the database level, although it does not replace installation of the package and its dependencies.

The README does not provide an installation command or a virtual environment setup. It links to the `pydantic-sqlalchemy` project on PyPI. Repository configuration requires Python 3.10 or later, so the first compatibility check belongs at the environment level rather than in the example code.

The user model declares four mapped fields:

python
Base = declarative_base()
engine = create_engine("sqlite://", echo=True)

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

The `User` declaration remains ordinary SQLAlchemy code. No Pydantic field syntax is added to it. This separation is the point of the library: SQLAlchemy remains the source definition, and Pydantic classes are generated from those source models.

A second entity introduces the relationship and foreign key:

python
class Address(Base):
    __tablename__ = "addresses"
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"))

    user = relationship("User", back_populates="addresses")

SQLAlchemy owns the persistence rules, including the foreign key and bidirectional relationship. Pydantic-SQLAlchemy does not replace them.

Declaring Relationship-Aware Pydantic Output

The README handles addresses through an explicit subclass rather than expecting the initial `PydanticUser` declaration to include every SQLAlchemy relationship. After generating the base Pydantic classes, the example adds a typed list of generated address models.

python
class PydanticUserWithAddresses(PydanticUser):
    addresses: List[PydanticAddress] = []

This produces a specialized API shape containing `addresses` alongside the fields inherited from `PydanticUser`. The design gives the application explicit control over which relationship belongs in a particular Pydantic model. It can also prevent unrelated ORM relationships from appearing in a response contract.

The trade-off is that relationship schemas require deliberate declarations. If an application needs several nested shapes, it may have to create several subclasses or otherwise arrange its generated models. The README does not document automatic recursive relationship conversion, relationship filtering options, or configuration for arbitrary nested objects.

The list default shown in the example is also part of the generated schema contract. Changing it would affect the resulting model rather than only SQLAlchemy persistence. An application should decide whether relationship data belongs in each interface before treating it as part of a public response.

The repository contains a `tests/` directory, but the README does not enumerate the full converter surface or guarantee behavior for cycles, multiple foreign keys, inheritance, aliases, or other advanced SQLAlchemy mappings. Those cases need direct examination before adoption.

Creating and Commiting SQLAlchemy Data

The example creates a session bound to the in-memory engine, constructs a `User`, and associates two `Address` objects. SQLAlchemy assigns the mapped relationships and prepares the objects for insertion.

python
LocalSession = sessionmaker(bind=engine)

db: Session = LocalSession()

ed_user = User(name="ed", fullname="Ed Jones", nickname="edsnickname")

address = Address(email_address="[email protected]")
address2 = Address(email_address="[email protected]")
ed_user.addresses = [address, address2]
db.add(ed_user)
db.commit()

This block performs no Pydantic conversion. That separation reflects the library's intended position. SQLAlchemy creates and commits database state; Pydantic-SQLAlchemy converts loaded objects when a Pydantic representation is required.

The example creates an in-memory database through SQLAlchemy metadata and then opens the session. Since the engine URL uses `sqlite://`, the database exists for the lifetime of that engine. Closing or recreating the engine would not provide the persistence behavior of a file-backed database.

The commit also establishes the values later expected by the assertions. IDs are generated by SQLite and SQLAlchemy for the inserted user and addresses. The tutorial later expects user ID `1`, address IDs `1` and `2`, and matching foreign key values.

For real applications, these mechanisms remain SQLAlchemy concerns. Pydantic-SQLAlchemy does not document transaction management, query construction, migration behavior, or connection handling because those operations stay outside its conversion role.

First Use with from_orm and dict

After committing the example data, the query retrieves the first `User`. That object is an SQLAlchemy ORM instance, which is the input expected by the generated Pydantic class's `from_orm()` method.

python
user = db.query(User).first()
pydantic_user = PydanticUser.from_orm(user)
data = pydantic_user.dict()
assert data == {
    "fullname": "Ed Jones",
    "id": 1,
    "name": "ed",
    "nickname": "edsnickname",
}

The resulting dictionary contains only the fields on `PydanticUser`. The `addresses` relationship does not appear because it was added only to `PydanticUserWithAddresses`. The relationship-aware conversion uses the same SQLAlchemy object but a different generated class.

python
pydantic_user_with_addresses = PydanticUserWithAddresses.from_orm(user)
data = pydantic_user_with_addresses.dict()
assert data == {
    "fullname": "Ed Jones",
    "id": 1,
    "name": "ed",
    "nickname": "edsnickname",
    "addresses": [
        {"email_address": "[email protected]", "id": 1, "user_id": 1},
        {"email_address": "[email protected]", "id": 2, "user_id": 1},
    ],
}

This is the first useful application boundary for the project: a SQLAlchemy entity can be loaded and converted before a handler, serializer, or service returns Pydantic-oriented data. The README does not provide installation commands beyond its PyPI link, nor does it document a command-line interface. Usage is through the Python API.

Dependency Limits, SQLModel, and Maintenance Boundaries

The package configuration defines narrow major-version ranges. It requires `SQLAlchemy >=1.3.16,<2.0.0` and `pydantic >=1.5.1,<2.0.0`, with Python 3.10 or later. These are concrete adoption boundaries. A project already standardized on SQLAlchemy 2.x or Pydantic 2.x cannot combine those major releases with the declared dependency set without resolving conflicts or changing the application's package choices.

The README explicitly recommends SQLModel for most uses. It describes SQLModel as solving the same problem through a different design. The README does not enumerate every additional problem SQLModel addresses, so its claims should not be expanded beyond that comparison. What is clear is the author's stated preference: use SQLModel for most cases rather than treating Pydantic-SQLAlchemy as the default model layer.

The trade-off is clearest for existing applications. Pydantic-SQLAlchemy may reduce duplicated field definitions around a SQLAlchemy 1.x model layer. SQLModel offers another way to define models that address the same integration need. For a new application, choosing Pydantic-SQLAlchemy means accepting dynamic generated types and the editor limitations identified in the README.

The repository is not archived, and its last push was on 2026-10-10. Release `0.0.10` was published on 2026-05-19. Earlier releases listed are `0.0.9` from 2021-05-30 and `0.0.8.post1` from 2020-11-23. The project uses the MIT license. The README calls the implementation experimental, and the release line remains pre-1.0. Upgrading across the major dependency boundaries is not supported by the declared package ranges.

Editorial conclusion

Pydantic-SQLAlchemy can serve as a bridge for an existing SQLAlchemy 1.x codebase that needs generated Pydantic 1.x models with limited static typing support. It is a poor fit for a new application, particularly one already committed to SQLAlchemy 2.x or Pydantic 2.x. Verify dependency compatibility first, then check whether dynamic models provide enough autocomplete and inline errors for the development workflow. For a new project without a legacy SQLAlchemy layer, the README's SQLModel recommendation is the more direct starting point.

Frequently asked questions

How do I install Pydantic-SQLAlchemy?

The README links to the `pydantic-sqlalchemy` package on PyPI but does not provide an installation command. Repository configuration requires Python 3.10 or later, SQLAlchemy from 1.3.16 up to but not including 2.0.0, and Pydantic from 1.5.1 up to but not including 2.0.0.

How do I convert a SQLAlchemy model with Pydantic-SQLAlchemy?

Pass the SQLAlchemy declarative class to `sqlalchemy_to_pydantic()`, then call `from_orm()` on a loaded ORM instance. The dictionary representation is obtained from that Pydantic instance with `.dict()`.

Does Pydantic-SQLAlchemy convert SQLAlchemy relationships automatically?

The README defines `PydanticUserWithAddresses` separately and adds an `addresses: List[PydanticAddress]` field to that subclass. The documented relationship output is therefore explicit rather than part of the basic `PydanticUser` conversion shown in the example.

Can Pydantic-SQLAlchemy be used with SQLAlchemy 2.x or Pydantic 2.x?

The declared dependencies require SQLAlchemy below 2.0.0 and Pydantic below 2.0.0. Compatibility with either newer major release is not included in the repository's package metadata.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. tiangolo/pydantic-sqlalchemy on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tiangolo-pydantic-sqlalchemy.svg)](https://hysenlabs.com/projects/tiangolo-pydantic-sqlalchemy)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tiangolo-pydantic-sqlalchemy.svg)](https://hysenlabs.com/projects/tiangolo-pydantic-sqlalchemy)

Was this useful?