-
-
Notifications
You must be signed in to change notification settings - Fork 742
Description
First Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn't find it.
- I searched the SQLModel documentation, with the integrated search.
- I already searched in Google "How to X in SQLModel" and didn't find any information.
- I already read and followed all the tutorial in the docs and didn't find an answer.
- I already checked if it is not related to SQLModel but to Pydantic.
- I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
- I commit to help with one of those options 👆
Example Code
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, validator
from sqlmodel import SQLModel
# NB: Inheriting from BaseModel!
class HeroUpdateA(BaseModel):
name: Optional[str]
secret_name: Optional[str]
age: Optional[int]
@validator("name", "secret_name", "age")
def reject_none(cls, v):
assert v is not None
return v
# NB: Inheriting from SQLModel!
class HeroUpdateB(SQLModel):
name: Optional[str]
secret_name: Optional[str]
age: Optional[int]
@validator("name", "secret_name", "age")
def reject_none(cls, v):
assert v is not None
return v
app = FastAPI()
# A PATCH request with payload {"age": 25} succeeds.
# name and secret_name are NOT validated.
@app.patch("/heroes-a/{id}")
async def patch_hero_a(obj: HeroUpdateA):
pass
# A PATCH request with payload {"age": 25} fails.
# name and secret_name are validated.
@app.patch("/heroes-b/{id}")
async def patch_hero_b(obj: HeroUpdateB):
pass
Description
Suppose we have a Hero(SQLModel, table=True)
model with no optional fields (so name
, secret_name
, and age
are not nullable in the underlying database). To properly support PATCH, a HeroUpdate model is needed with all fields optional, because we don't know in advance what field(s) a user is going to supply.
Since the fields are not nullable, we want a validator to reject explicit JSON null values, e.g. a payload like this: {"name": null, "age": 25}
, otherwise our user might hit an Internal Server Error (HTTP 500) upon saving a hero to the database. These links propose a clever solution to this problem by writing a validator:
- How to have an “optional” field but if present required to conform to non None value? pydantic/pydantic#1223
- Is it possible to fail validation if an Optional field has value as null in json ? fastapi#3776
This solution works, because, by default, validators aren't called on arguments that are not provided. So a payload like {"age": 25}
is fine and will not trigger validation of name
and secret_name
, even though these fields have None
as a default.
To my surprise, however, the proposed solution does not work when inheriting from SQLModel, only when inheriting from BaseModel. In case of SQLModel, validation always fires, even when a field is not explicitly provided. Since SQLModel is powered by Pydantic, I expected similar behaviour.
If not a bug, what would be an elegant way to accomplish validation like this using SQLModel?
Operating System
Linux
Operating System Details
I'm using this Docker image: tiangolo/uvicorn-gunicorn-fastapi:python3.8-slim
SQLModel Version
0.0.6
Python Version
3.8.12
Additional Context
No response