8000 How to validate JSON null values for optional fields? · Issue #230 · fastapi/sqlmodel · GitHub
[go: up one dir, main page]

Skip to content
How to validate JSON null values for optional fields? #230
@byrman

Description

@byrman

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:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    answeredbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0