pythonfastapisqlmodel

Why FastAPI isn't validating POST body?


I'm working on a webserver built with FastAPI version 0.111.0 and SQLModel version 0.0.18.

When I'm calling the POST endpoint with partial data or with keys I don't specify in my class, it doesn't trigger the unprocessable entity or bad request error. I'm using the method POST only for inserting data and not updating it. For update an entity, I use a separate PUT request.

Here's my model:

from sqlmodel import SQLModel, Field

class Job(SQLModel, table=True):
    job_id: int = Field(primary_key=True)
    title: str
    start_time: str
    end_time: str | None = None
    pipeline_id: int = Field(foreign_key="pipeline.pipeline_id")
    prj_path: str
    branch_tag: str
    user: str
    status: str
    log: str = ""
    result: str = ""

and here's the controller:

from fastapi import APIRouter
from models.job import Job


job_router = APIRouter(prefix="/job", tags=["Jobs"])

@job_router.post("/", status_code=201, response_model=Job)
async def add_job(job: Job):
    job = await c.insert_job(job)
    return job

The c.insert_job(job) is where I'm saving the object in the database and the job_router is importend in the main file with the FastAPI app:

from fastapi import FastAPI
from routers.job import job_router

app = FastAPI()

app.include_router(job_router)

Even if the auto-created documentation tells me that there are some required fields as shown here:
job class in swagger
if I'm sending a request with a body like { "job_id": 0 } or even { "job_id": 0, "test": "test" }, it passes without triggering anything like shown in the next image (here I return the partial object I get instead of adding it to the database because it would throw an error on fields without the default value):
returned object


Solution

  • Use this model for response validation

        from pydantic import BaseModel
        from typing import Optional
        class JobRead(BaseModel):
            job_id: int
            title: str
            start_time: str
            end_time: Optional[str] = Field(default=None)
            pipeline_id: int
            prj_path: str
            branch_tag: str
            user: str
            status: str
            log: Optional[str] = Field(default="")
            result: Optional[str] = Field(default="")
    

    And you can do use Pydantic validation about validation https://docs.pydantic.dev/latest/#why-use-pydantic

    or use Depends() with Pydantic model

    from fastapi import APIRouter
    from models.job import Job
    
    
    job_router = APIRouter(prefix="/job", tags=["Jobs"])
    
    @job_router.post("/", status_code=201, response_model=Job)
    async def add_job(job: Job = Depends()):
        job = await c.insert_job(job)
        return job
    

    about Depends https://fastapi.tiangolo.com/tutorial/dependencies/