Fix role_id sentinel bug: use _UNSET sentinel so None explicitly clears FK, missing param leaves unchanged
This commit is contained in:
@@ -11,6 +11,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.core.auth import hash_password
|
||||
from app.models.user import User, UserTenant
|
||||
|
||||
# Sentinel used to distinguish "not provided" from "explicitly set to None".
|
||||
# When ``role_id`` is _UNSET the service leaves the existing value untouched.
|
||||
# When ``role_id`` is None the service clears the FK (falls back to role string).
|
||||
_UNSET: Any = object()
|
||||
|
||||
|
||||
class UserService:
|
||||
"""Handles user CRUD operations."""
|
||||
@@ -108,14 +113,15 @@ class UserService:
|
||||
user_id: uuid.UUID,
|
||||
name: str | None = None,
|
||||
role: str | None = None,
|
||||
role_id: uuid.UUID | None = None,
|
||||
role_id: uuid.UUID | None | Any = _UNSET,
|
||||
is_active: bool | None = None,
|
||||
) -> User | None:
|
||||
"""Update a user.
|
||||
|
||||
``role_id`` accepts a UUID to link a custom Role, or None to leave
|
||||
the existing value unchanged. Pass ``role_id`` explicitly as None
|
||||
combined with the sentinel logic below to clear it.
|
||||
``role_id`` uses a sentinel to distinguish three states:
|
||||
- ``_UNSET`` (default): leave the existing role_id unchanged
|
||||
- ``None``: clear the FK (fall back to the legacy ``role`` string)
|
||||
- ``uuid.UUID``: link to a custom Role record
|
||||
"""
|
||||
q = select(User).where(User.id == user_id, User.tenant_id == tenant_id)
|
||||
result = await db.execute(q)
|
||||
@@ -127,7 +133,7 @@ class UserService:
|
||||
user.name = name
|
||||
if role is not None:
|
||||
user.role = role
|
||||
if role_id is not None:
|
||||
if role_id is not _UNSET:
|
||||
user.role_id = role_id
|
||||
if is_active is not None:
|
||||
user.is_active = is_active
|
||||
|
||||
Reference in New Issue
Block a user