Files
maziggy 8e5116fe20 fix(api): null for a field that can't be empty leaves it unchanged
Update schemas declare fields `X | None = None` so a PATCH can leave
them out, and most routes treat null as "unchanged". About a dozen
(printers, smart plugs, queue items and bulk edits, archives,
filaments, spools, notification providers, GitHub backup, HA sensors,
maintenance, external links) applied model_dump(exclude_unset=True)
with setattr instead, wrote the null into a NOT NULL column and failed
with a 500.

A new base, IgnoresNullForRequiredColumns, drops such a null before
validation, using the NOT NULL flags of the table the schema names
(__orm_table__, resolved lazily), so every route treats it as not sent.
A null for a nullable column still clears it. 40 update schemas use it;
a guard test requires every PATCH/PUT body that writes a row to use it
or to be listed with a reason (suppliers keep their own 422 for a null
name).
2026-10-11 12:48:48 +02:00

60 lines
1.5 KiB
Python

from datetime import datetime
from pydantic import BaseModel, Field
from backend.app.schemas.not_null import IgnoresNullForRequiredColumns
class FilamentBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
type: str = Field(..., min_length=1, max_length=50)
brand: str | None = None
color: str | None = None
color_hex: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
cost_per_kg: float = 25.0
spool_weight_g: float = 1000.0
currency: str = "USD"
density: float | None = None
print_temp_min: int | None = None
print_temp_max: int | None = None
bed_temp_min: int | None = None
bed_temp_max: int | None = None
class FilamentCreate(FilamentBase):
pass
class FilamentUpdate(IgnoresNullForRequiredColumns):
__orm_table__ = "filaments"
name: str | None = None
type: str | None = None
brand: str | None = None
color: str | None = None
color_hex: str | None = None
cost_per_kg: float | None = None
spool_weight_g: float | None = None
currency: str | None = None
density: float | None = None
print_temp_min: int | None = None
print_temp_max: int | None = None
bed_temp_min: int | None = None
bed_temp_max: int | None = None
class FilamentResponse(FilamentBase):
id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class FilamentCostCalculation(BaseModel):
filament_id: int
filament_name: str
weight_grams: float
cost: float
currency: str