Add color validation for categories
This commit is contained in:
parent
e0b77d7ff2
commit
8737897ce9
|
@ -15,6 +15,7 @@ dependencies = [
|
|||
"python-jose>=3.3.0",
|
||||
"python-multipart>=0.0.17",
|
||||
"bcrypt>=4.2.1",
|
||||
"pydantic-extra-types>=2.10.1",
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">= 3.12"
|
||||
|
|
|
@ -72,8 +72,11 @@ pydantic==2.10.2
|
|||
# via event-management
|
||||
# via fastapi
|
||||
# via lazy-model
|
||||
# via pydantic-extra-types
|
||||
pydantic-core==2.27.1
|
||||
# via pydantic
|
||||
pydantic-extra-types==2.10.1
|
||||
# via event-management
|
||||
pymongo==4.9.2
|
||||
# via motor
|
||||
python-decouple==3.8
|
||||
|
@ -101,6 +104,7 @@ typing-extensions==4.12.2
|
|||
# via fastapi
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
# via pydantic-extra-types
|
||||
uvicorn==0.32.1
|
||||
# via event-management
|
||||
virtualenv==20.28.0
|
||||
|
|
|
@ -56,8 +56,11 @@ pydantic==2.10.2
|
|||
# via event-management
|
||||
# via fastapi
|
||||
# via lazy-model
|
||||
# via pydantic-extra-types
|
||||
pydantic-core==2.27.1
|
||||
# via pydantic
|
||||
pydantic-extra-types==2.10.1
|
||||
# via event-management
|
||||
pymongo==4.9.2
|
||||
# via motor
|
||||
python-decouple==3.8
|
||||
|
@ -83,5 +86,6 @@ typing-extensions==4.12.2
|
|||
# via fastapi
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
# via pydantic-extra-types
|
||||
uvicorn==0.32.1
|
||||
# via event-management
|
||||
|
|
|
@ -3,7 +3,8 @@ from typing import Annotated, final
|
|||
|
||||
from beanie import PydanticObjectId
|
||||
from fastapi import APIRouter, Body, HTTPException, Response, status
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, StringConstraints, field_validator
|
||||
from pydantic_extra_types.color import Color
|
||||
|
||||
from src.api.auth.dependencies import LoggedInDep
|
||||
from src.db.models.category import Category
|
||||
|
@ -21,13 +22,26 @@ categories_router = APIRouter(tags=["Categories"], prefix="/categories", depende
|
|||
base_router = APIRouter(tags=["Categories"], dependencies=[LoggedInDep])
|
||||
|
||||
|
||||
class _BaseCategoryData(BaseModel):
|
||||
"""Base class for all category data classes."""
|
||||
|
||||
name: Annotated[str, StringConstraints(max_length=50)]
|
||||
color: Color
|
||||
|
||||
@field_validator("color", mode="after")
|
||||
@classmethod
|
||||
def validate_color(cls, value: Color) -> Color:
|
||||
"""Validate the color."""
|
||||
if len(value.as_rgb_tuple()) == 4:
|
||||
raise ValueError("Alpha channel is not allowed in colors")
|
||||
return value
|
||||
|
||||
|
||||
@final
|
||||
class CategoryData(BaseModel):
|
||||
class CategoryData(_BaseCategoryData):
|
||||
"""Data about a category sent to the user."""
|
||||
|
||||
owner_user_id: PydanticObjectId
|
||||
name: str
|
||||
color: str
|
||||
created_at: datetime
|
||||
|
||||
@classmethod
|
||||
|
@ -37,26 +51,23 @@ class CategoryData(BaseModel):
|
|||
raise ValueError("Got a category without id")
|
||||
|
||||
return cls(
|
||||
owner_user_id=category.id,
|
||||
name=category.name,
|
||||
color=category.color,
|
||||
color=Color(category.color),
|
||||
owner_user_id=category.id,
|
||||
created_at=category.created_at,
|
||||
)
|
||||
|
||||
|
||||
@final
|
||||
class CategoryCreateData(BaseModel):
|
||||
class CategoryCreateData(_BaseCategoryData):
|
||||
"""Data necessary to create a new category.
|
||||
|
||||
This structure is intended to be used for POST & PUT requests.
|
||||
"""
|
||||
|
||||
name: str
|
||||
color: str
|
||||
|
||||
async def create_category(self, user: User) -> Category:
|
||||
"""Create a new category in the database."""
|
||||
cat = Category(user=user, name=self.name, color=self.color)
|
||||
cat = Category(user=user, name=self.name, color=self.color.as_hex(format="long"))
|
||||
return await cat.create()
|
||||
|
||||
async def update_category(self, category: Category) -> Category:
|
||||
|
@ -67,7 +78,7 @@ class CategoryCreateData(BaseModel):
|
|||
category.name = self.name
|
||||
updated = True
|
||||
if category.color != self.color:
|
||||
category.color = self.color
|
||||
category.color = self.color.as_hex(format="long")
|
||||
updated = True
|
||||
|
||||
if updated:
|
||||
|
@ -76,14 +87,14 @@ class CategoryCreateData(BaseModel):
|
|||
|
||||
|
||||
@final
|
||||
class PartialCategoryUpdateData(BaseModel):
|
||||
class PartialCategoryUpdateData(_BaseCategoryData):
|
||||
"""Data necessary to perform a partial update of the category.
|
||||
|
||||
This structure is intended to be used for PATCH requests.
|
||||
"""
|
||||
|
||||
name: str | None = None
|
||||
color: str | None = None
|
||||
name: Annotated[str, StringConstraints(max_length=50)] | None = None # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
color: Color | None = None # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
|
||||
async def update_category(self, category: Category) -> Category:
|
||||
"""Update an existing category, overwriting it with data that were specified."""
|
||||
|
@ -93,7 +104,7 @@ class PartialCategoryUpdateData(BaseModel):
|
|||
category.name = self.name
|
||||
updated = True
|
||||
if self.color and category.color != self.color:
|
||||
category.color = self.color
|
||||
category.color = self.color.as_hex(format="long")
|
||||
updated = True
|
||||
|
||||
if updated:
|
||||
|
|
Loading…
Reference in a new issue