Fix route: use model_fields_set to pass _UNSET when role_id not sent, None when explicitly null
This commit is contained in:
+19
-5
@@ -13,7 +13,7 @@ from app.core.db import get_db
|
|||||||
from app.core.notifications import create_notification
|
from app.core.notifications import create_notification
|
||||||
from app.deps import get_current_user, require_admin
|
from app.deps import get_current_user, require_admin
|
||||||
from app.schemas.user import UserCreate, UserUpdate
|
from app.schemas.user import UserCreate, UserUpdate
|
||||||
from app.services.user_service import user_service
|
from app.services.user_service import user_service, _UNSET
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/v1/users", tags=["users"])
|
router = APIRouter(prefix="/api/v1/users", tags=["users"])
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ router = APIRouter(prefix="/api/v1/users", tags=["users"])
|
|||||||
def _parse_role_id(raw: str | None) -> uuid.UUID | None:
|
def _parse_role_id(raw: str | None) -> uuid.UUID | None:
|
||||||
"""Convert a string body value into a UUID or None.
|
"""Convert a string body value into a UUID or None.
|
||||||
|
|
||||||
Empty string and None are both treated as "no role_id".
|
Empty string and None are both treated as "clear role_id".
|
||||||
"""
|
"""
|
||||||
if raw is None or raw == "":
|
if raw is None or raw == "":
|
||||||
return None
|
return None
|
||||||
@@ -138,7 +138,12 @@ async def update_user(
|
|||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: dict = Depends(require_admin),
|
current_user: dict = Depends(require_admin),
|
||||||
):
|
):
|
||||||
"""Update a user (admin only)."""
|
"""Update a user (admin only).
|
||||||
|
|
||||||
|
Uses ``model_fields_set`` to detect whether ``role_id`` was explicitly
|
||||||
|
present in the request body (even if sent as ``null``). This allows
|
||||||
|
the caller to clear the FK by sending ``role_id: null``.
|
||||||
|
"""
|
||||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||||
acting_user_id = uuid.UUID(current_user["user_id"])
|
acting_user_id = uuid.UUID(current_user["user_id"])
|
||||||
try:
|
try:
|
||||||
@@ -148,17 +153,26 @@ async def update_user(
|
|||||||
400, detail={"detail": "Invalid user_id", "code": "invalid_id"}
|
400, detail={"detail": "Invalid user_id", "code": "invalid_id"}
|
||||||
) from None
|
) from None
|
||||||
|
|
||||||
|
# Determine if role_id was explicitly sent (Pydantic v2)
|
||||||
|
role_id_sent = "role_id" in body.model_fields_set
|
||||||
|
|
||||||
changes: dict[str, Any] = {}
|
changes: dict[str, Any] = {}
|
||||||
if body.name is not None:
|
if body.name is not None:
|
||||||
changes["name"] = body.name
|
changes["name"] = body.name
|
||||||
if body.role is not None:
|
if body.role is not None:
|
||||||
changes["role"] = body.role
|
changes["role"] = body.role
|
||||||
if body.role_id is not None:
|
if role_id_sent:
|
||||||
changes["role_id"] = body.role_id
|
changes["role_id"] = body.role_id
|
||||||
if body.is_active is not None:
|
if body.is_active is not None:
|
||||||
changes["is_active"] = body.is_active
|
changes["is_active"] = body.is_active
|
||||||
|
|
||||||
role_id = _parse_role_id(body.role_id)
|
# Pass _UNSET sentinel when role_id was not in the request body
|
||||||
|
# so the service leaves the existing value untouched.
|
||||||
|
role_id: uuid.UUID | None | Any
|
||||||
|
if role_id_sent:
|
||||||
|
role_id = _parse_role_id(body.role_id)
|
||||||
|
else:
|
||||||
|
role_id = _UNSET
|
||||||
|
|
||||||
user = await user_service.update_user(
|
user = await user_service.update_user(
|
||||||
db,
|
db,
|
||||||
|
|||||||
Reference in New Issue
Block a user