I've noticed recently that there is practically no support for type-safe
ORM/ODM projections in the broader Python ecosystem.
A little context: ORMs (object-relational mapper) and ODM (object-document
mapper/mapping) are tools or libraries for interacting with databases.
SQLAlchemy and the Django ORM are probably the most famous examples.
Basically, you model a database table as a class, and rows in the table are
instances of that class. ODMs are a little simpler than ORMs, they
practically only map rows/documents to instances of a class, ORMs are more
Writing a type-safe ODM nowadays is super simple, and there are loads out
there. The idea is this:
user = await fetch(User, id=1)
This can be made to work with the proper type annotations without much fuss.
Now, the issue is doing projections in a type-safe manner. A projection
basically means loading only a subset of the fields, usually for
performance. Let's say that we're only interested in the user username
(imagine there are 30 other fields in the class that we don't care about).
That would look kinda like:
from dataclasses import fields
user_projection: tuple[str] = await fetch_projection(User, id=1,
This can't really be very type-safe since Mypy treats `fields(User)` as
`dataclasses.Field*[Any]`. Now, if Mypy treated it as
`dataclasses.Field*[str]`, I assume that would be a different story, and
the function could be annotated to return a 1-tuple of `str`.
As mentioned, I've looked at a bunch of ORM/ODM libraries (and written a
few internally) and as far as I can tell this use-case is very much
unsupported as of yet. (I would appreciate counter-examples, obviously!)
This made me sad so I came to the list to see if there's anything to be
To be super honest I'm more interested in getting attrs support for this,
but attrs and dataclasses are so similar I figured if someone did the work
in the dataclass plugin, the logic could also be ported over to the attrs
plugin. attrs also has a nicer API for actually getting the fields, so the
equivalent attrs example would be:
from attrs import fields as f
user_projection = await fetch_projection(User, id=1, f(User).username)
Wouldn't it be super cool if Mypy (or other type checkers) could check this