prisma-client-py icon indicating copy to clipboard operation
prisma-client-py copied to clipboard

Add support for selecting fields

Open RobertCraigie opened this issue 4 years ago • 1 comments

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):
  ...

RobertCraigie avatar Jun 14 '21 18:06 RobertCraigie

An interesting potential solution would be to make use of PEP 646 which would mean returning tuples instead of BaseModels though.

RobertCraigie avatar Jan 30 '22 08:01 RobertCraigie

Selecting fields is really helpful, hope this feature will be implemented soon, thank you.

AndersonatAMC avatar Dec 10 '22 19:12 AndersonatAMC

Is model field selection available ?

lekhnath avatar Mar 08 '23 12:03 lekhnath

@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/

RobertCraigie avatar Mar 08 '23 12:03 RobertCraigie