Skip to content

Commit f1da544

Browse files
committed
added validation dto decorator
1 parent 29a1b89 commit f1da544

File tree

5 files changed

+80
-15
lines changed

5 files changed

+80
-15
lines changed

fastapi_project/api/v1/user.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
from fastapi_project.utils.base import the_query
55
from fastapi_project.services.user_service import UserService
66
from fastapi_project.schemas.user_schema import UserCreateSchema
7-
7+
from fastapi_project.utils.validation import dto
88
router = APIRouter()
99

1010
user_service = UserService()
1111

1212
@router.post("/users/create")
13-
async def create_order(request: Request, the_data: UserCreateSchema):
13+
@dto(UserCreateSchema)
14+
async def create_order(request: Request):
1415
# Retrieve data from the request
1516
request_data = await the_query(request)
1617
data = UserCreateSchema(**request_data)

fastapi_project/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from dotenv import load_dotenv
44
from fastapi.middleware.cors import CORSMiddleware
55
from fastapi_project.api import health, root_index
6+
from fastapi_project.utils.validation import setup_validation_exception_handler
67

78
# Load .env file
89
load_dotenv()
@@ -13,6 +14,7 @@
1314

1415
def create_application():
1516
application = FastAPI()
17+
setup_validation_exception_handler(application)
1618

1719
# Include the root index and health router
1820
application.include_router(root_index.router)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
from pydantic import BaseModel, EmailStr
1+
from pydantic import BaseModel, EmailStr, Field
22
from typing import Optional
33

44
# Pydantic model for creating a new user
55
class UserCreateSchema(BaseModel):
66
name: str
77
email: EmailStr
8-
password: str
8+
password: str = Field(..., min_length=8)
99

1010

1111
# Pydantic model for updating user data
1212
class UserUpdateSchema(BaseModel):
1313
name: Optional[str] = None
1414
email: Optional[EmailStr] = None
15-
password: Optional[str] = None
15+
password: Optional[str] = Field(None, min_length=8)

fastapi_project/utils/base.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from fastapi import Request
1+
from fastapi import Request, HTTPException
22
from typing import Any, Dict, Optional, Union
33
from pydantic import BaseModel, ValidationError
44
from sqlalchemy import desc
55
from urllib.parse import urlparse
6+
from fastapi.responses import JSONResponse
7+
from fastapi import FastAPI
68

79
async def the_query(request: Request, name = None) -> Dict[str, str]:
810
data = {}
@@ -21,17 +23,22 @@ async def the_query(request: Request, name = None) -> Dict[str, str]:
2123

2224

2325
async def validate_data(data: Dict[str, Any], model: BaseModel) -> Dict[str, Union[str, Dict[str, Any]]]:
24-
output = {'status': 'valid'}
25-
2626
try:
2727
instance = model(**data)
28-
output['data'] = instance.dict()
28+
return {'success': True, 'data': instance.dict()}
2929
except ValidationError as e:
30-
# If validation fails, return status as invalid and the validation errors
31-
output['status'] = 'invalid'
32-
output['errors'] = e.errors()
33-
34-
return output
30+
errors = {}
31+
for error in e.errors():
32+
field = error['loc'][0]
33+
message = field + " " + error['msg']
34+
if field not in errors:
35+
errors[field] = []
36+
errors[field].append(message)
37+
38+
return {
39+
'success': False,
40+
'errors': errors["details"]
41+
}
3542

3643

3744
def the_sorting(request, query):
@@ -96,4 +103,8 @@ def paginate(request: Request, query, serilizer, the_page: int = 1, the_per_page
96103
'from': offset + 1 if data else None,
97104
'to': offset + len(data) if data else None,
98105
wrap: data
99-
}
106+
}
107+
108+
109+
# Update the __all__ list
110+
__all__ = []

fastapi_project/utils/validation.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from fastapi import Request, HTTPException
2+
from fastapi.responses import JSONResponse
3+
from fastapi import FastAPI
4+
from pydantic import BaseModel, ValidationError
5+
from functools import wraps
6+
from fastapi_project.utils.base import the_query
7+
8+
# Add the ValidationException class
9+
class ValidationException(Exception):
10+
def __init__(self, errors: dict):
11+
self.errors = errors
12+
13+
# Add the setup_validation_exception_handler function
14+
def setup_validation_exception_handler(app: FastAPI):
15+
@app.exception_handler(ValidationException)
16+
async def validation_exception_handler(request: Request, exc: ValidationException):
17+
return JSONResponse(
18+
status_code=422,
19+
content=exc.errors
20+
)
21+
22+
# Add the dto decorator
23+
def dto(schema: BaseModel):
24+
def decorator(func):
25+
@wraps(func)
26+
async def wrapper(request: Request, *args, **kwargs):
27+
try:
28+
request_data = await the_query(request)
29+
validated_data = schema(**request_data)
30+
return await func(validated_data, *args, **kwargs)
31+
except ValidationError as e:
32+
errors = {}
33+
for error in e.errors():
34+
field = error['loc'][0]
35+
message = field + " " + error['msg']
36+
if field not in errors:
37+
errors[field] = []
38+
errors[field].append(message)
39+
40+
raise ValidationException({
41+
'success': False,
42+
'errors': errors
43+
})
44+
except ValueError:
45+
raise HTTPException(status_code=400, detail="Invalid JSON")
46+
47+
return wrapper
48+
return decorator
49+
50+
# Update the __all__ list
51+
__all__ = ['dto', 'ValidationException', 'setup_validation_exception_handler']

0 commit comments

Comments
 (0)