Add support for selecting fields
Problem
A crucial part of modern and performant ORMs is the ability to choose what fields are returned, Prisma Client Python is currently missing this feature.
Mypy solution
As we have a mypy plugin we can dynamically modify types on the fly, this means we would be able to make use of a more ergonomic solution.
class Model(BaseModel):
id: str
name: str
points: Optional[int]
class SelectedModel(BaseModel):
id: Optional[str]
name: Optional[str]
points: Optional[int]
ModelSelect = Iterable[Literal['id', 'name', 'points']]
@overload
def action(
...
) -> Model:
...
@overload
def action(
...
select: ModelSelect
) -> SelectedModel:
...
model = action(select={'id', 'name'})
The mypy plugin would then dynamically remove the Optional from the model for every field that is selected, we might also be able to remove the fields that aren't selected although I don't know if this is possible.
The downside to a solution like this is that unreachable code will not trigger an error when type checking with a type checker other than mypy, e.g.
user = await client.user.find_first(select={'name'})
if user.id is not None:
print(user.id)
Will pass type checks although the if block will never be ran.
Type checker agnostic solution
After #59 is implemented the query builder should only select the fields that are present on the given BaseModel.
This would mean that users could generate partial types and then easily use them to select certain fields.
User.create_partial('UserOnlyName', include={'name'})
from prisma.partials import UserOnlyName
user = await UserOnlyName.prisma().find_unique(where={'id': 'abc'})
Or create models by themselves
class User(BaseUser):
name: str
user = await User.prisma().find_unique(where={'id': 'abc'})
This will make typing generic functions to process models more difficult, for example, the following function would not accept custom models.:
def process_user(user: User) -> None:
...
It could however be modified to accept objects with the correct properties by using a Protocol.
class UserWithID(Protocol):
id: str
def process_user(user: UserWithID):
...
An interesting potential solution would be to make use of PEP 646 which would mean returning tuples instead of BaseModels though.
Selecting fields is really helpful, hope this feature will be implemented soon, thank you.
Is model field selection available ?
@lekhnath Yes the solution mentioned in the "Type checker agnostic" section has been implemented, see these docs for more information: https://prisma-client-py.readthedocs.io/en/stable/reference/selecting-fields/