Open RobertCraigie opened 3 years ago
This would be super helpful, it's also difficult to implement the Json field into my workflow.
Here's an example of what I'm trying to do:
# prisma/prisma.schema
model User {
id String @id @default(cuid())
name String
scores Json
}
# prisma/partial_types.py
from prisma.models import User
User.create_partial("UserWriteBase", exclude={"id"}, exclude_relational_fields=True)
I'm using Pydantic to validate the field when I load it in via FastAPI.
# model.py
from pydantic import BaseModel
from prisma.partials import UserWriteBase
class ScoresModel(BaseModel):
score1: int = 0
score2: int = 0
class UserWrite(UserWriteBase):
scores: Optional[ScoresModel]
# api.py
@router.post("/user", response_model=UserRead)
async def create_user(user: UserWrite, db: Prisma = Depends(get_db)):
"""Create a user."""
data = user.dict(exclude_unset=True)
if "scores" in data:
data["scores"] = Json(data["scores"])
return await db.target.create(data=data) # type:ignore
I'm not sure if this is the best way to do this but it's a bit of a pain to have to manually cast the field to a Json
object.
Also, I have to use # type:ignore
because I get this error when loading the data, not sure if there's a better way to get rid of this.
Argument of type "DictStrAny" cannot be assigned to parameter "data" of type "UserUpdateInput" in function "update"
"DictStrAny" is incompatible with "UserUpdateInput"Pylance[reportGeneralTypeIssues](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportGeneralTypeIssues)
Lastly, it would be super helpful if create_partial
allowed me to override a field configuration directly. I've looked though and Field
doesn't support the ability to pass type
or annotation
so it would have to be hacked in and isn't that big of a deal. This is really only useful for Json
fields so I figured I'd put it here.
# prisma/partial_types.py
from prisma.models import User
from pydantic import BaseModel, Field
class ScoresModel(BaseModel):
score1: Optional[int]
score2: Optional[int]
User.create_partial("UserWriteBase", exclude={"id"}, exclude_relational_fields=True, override={"scores": Field(type=Optional[ScoresModel])})
I'm not sure if this is the best way to do this but it's a bit of a pain to have to manually cast the field to a Json object.
Yeah this is a pretty reasonable approach.
The reason you have to make the field a Json
object is essentially a limitation of the internal Prisma query builder implementation. It needs to know that the field is a json type so that it serializes it to a JSON string. See #99 for fixing this.
Also, I have to use # type:ignore because I get this error when loading the data, not sure if there's a better way to get rid of this.
This is a limitation of the Python typing system unfortunately. Even something like this will generate a type error:
data = {
'name': 'Robert',
'scores': Json(...)
}
await db.target.create(data=data)
This is because the type of data
is inferred to dict[str, str | Json]
which is incompatible with the TypedDict type that we generate.
To properly fix this you have to provide an explicit type hint, e.g.
data: prisma.types.TargetCreateInput = {
'name': 'Robert',
'scores': Json(...)
}
await db.target.create(data=data)
This won't work with your code though as you're constructing the initial dictionary dynamically.
You could rewrite your code like this to be type safe:
async def create_user(user: UserWrite, db: Prisma = Depends(get_db)):
"""Create a user."""
data = user.dict(exclude_unset=True)
if "scores" in data:
data["scores"] = Json(data["scores"])
return await db.target.create(
data=prisma.validate(
prisma.types.TargetCreateInput,
data,
)
)
Also I'd recommend not using # type: ignore
unless you have to as it can mask other errors. e.g. say you rename the model in the Prisma schema or rename the db
variable, Pyright won't report any errors. Instead you should use cast(Any, ...)
as that means all other errors will still be caught!
Problem
Currently the following code will generate a slew of type errors.
While I do think that naively working with JSON objects should raise type errors, it should be easy to cast the data to the expected types.
For example, the above code written in a type safe manner would look like this:
However there are a few problems with this:
Suggested solution
Once #59 is implemented, this would be easier, for example, this should work:
And for client based access, #24 would make this easier, for example:
The purpose of this issue is for tracking the aforementioned issues and documentation.