mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:51:08 -05:00
feat: Add PrimeProgress, PrimeSelect, and PrimeSkeleton components with customizable styles and props
- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color). - Created PrimeSelect component with dropdown functionality, custom templates, and validation states. - Developed PrimeSkeleton component for loading placeholders with different shapes and animations. - Updated index.ts to export new components for easy import. - Enhanced PrimeVueTest.vue to include tests for new components and their functionalities. - Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles. - Added ambient type declarations for various components to improve TypeScript support.
This commit is contained in:
@@ -5,16 +5,40 @@ This module provides a unified interface to all serializers across different dom
|
||||
while maintaining the modular structure for better organization and maintainability.
|
||||
"""
|
||||
|
||||
# Shared utilities and base classes
|
||||
from .services import (
|
||||
HealthCheckOutputSerializer,
|
||||
PerformanceMetricsOutputSerializer,
|
||||
SimpleHealthOutputSerializer,
|
||||
EmailSendInputSerializer,
|
||||
EmailTemplateOutputSerializer,
|
||||
MapDataOutputSerializer,
|
||||
CoordinateInputSerializer,
|
||||
HistoryEventSerializer,
|
||||
HistoryEntryOutputSerializer,
|
||||
HistoryCreateInputSerializer,
|
||||
ModerationSubmissionSerializer,
|
||||
ModerationSubmissionOutputSerializer,
|
||||
RoadtripParkSerializer,
|
||||
RoadtripCreateInputSerializer,
|
||||
RoadtripOutputSerializer,
|
||||
GeocodeInputSerializer,
|
||||
GeocodeOutputSerializer,
|
||||
DistanceCalculationInputSerializer,
|
||||
DistanceCalculationOutputSerializer,
|
||||
) # noqa: F401
|
||||
from typing import Any, Dict, List
|
||||
import importlib
|
||||
|
||||
# --- Shared utilities and base classes ---
|
||||
from .shared import (
|
||||
CATEGORY_CHOICES,
|
||||
ModelChoices,
|
||||
LocationOutputSerializer,
|
||||
CompanyOutputSerializer,
|
||||
UserModel,
|
||||
)
|
||||
) # noqa: F401
|
||||
|
||||
# Parks domain
|
||||
# --- Parks domain ---
|
||||
from .parks import (
|
||||
ParkListOutputSerializer,
|
||||
ParkDetailOutputSerializer,
|
||||
@@ -29,9 +53,9 @@ from .parks import (
|
||||
ParkLocationUpdateInputSerializer,
|
||||
ParkSuggestionSerializer,
|
||||
ParkSuggestionOutputSerializer,
|
||||
)
|
||||
) # noqa: F401
|
||||
|
||||
# Companies and ride models domain
|
||||
# --- Companies and ride models domain ---
|
||||
from .companies import (
|
||||
CompanyDetailOutputSerializer,
|
||||
CompanyCreateInputSerializer,
|
||||
@@ -39,9 +63,9 @@ from .companies import (
|
||||
RideModelDetailOutputSerializer,
|
||||
RideModelCreateInputSerializer,
|
||||
RideModelUpdateInputSerializer,
|
||||
)
|
||||
) # noqa: F401
|
||||
|
||||
# Rides domain
|
||||
# --- Rides domain ---
|
||||
from .rides import (
|
||||
RideParkOutputSerializer,
|
||||
RideModelOutputSerializer,
|
||||
@@ -59,217 +83,10 @@ from .rides import (
|
||||
RideReviewOutputSerializer,
|
||||
RideReviewCreateInputSerializer,
|
||||
RideReviewUpdateInputSerializer,
|
||||
)
|
||||
) # noqa: F401
|
||||
|
||||
# Accounts domain
|
||||
from .accounts import (
|
||||
UserProfileOutputSerializer,
|
||||
UserProfileCreateInputSerializer,
|
||||
UserProfileUpdateInputSerializer,
|
||||
TopListOutputSerializer,
|
||||
TopListCreateInputSerializer,
|
||||
TopListUpdateInputSerializer,
|
||||
TopListItemOutputSerializer,
|
||||
TopListItemCreateInputSerializer,
|
||||
TopListItemUpdateInputSerializer,
|
||||
UserOutputSerializer,
|
||||
LoginInputSerializer,
|
||||
LoginOutputSerializer,
|
||||
SignupInputSerializer,
|
||||
SignupOutputSerializer,
|
||||
PasswordResetInputSerializer,
|
||||
PasswordResetOutputSerializer,
|
||||
PasswordChangeInputSerializer,
|
||||
PasswordChangeOutputSerializer,
|
||||
LogoutOutputSerializer,
|
||||
SocialProviderOutputSerializer,
|
||||
AuthStatusOutputSerializer,
|
||||
)
|
||||
|
||||
# Statistics and health checks
|
||||
from .other import (
|
||||
ParkStatsOutputSerializer,
|
||||
RideStatsOutputSerializer,
|
||||
ParkReviewOutputSerializer,
|
||||
HealthCheckOutputSerializer,
|
||||
PerformanceMetricsOutputSerializer,
|
||||
SimpleHealthOutputSerializer,
|
||||
)
|
||||
|
||||
# Media domain
|
||||
from .media import (
|
||||
PhotoUploadInputSerializer,
|
||||
PhotoDetailOutputSerializer,
|
||||
PhotoListOutputSerializer,
|
||||
PhotoUpdateInputSerializer,
|
||||
)
|
||||
|
||||
# Parks media domain
|
||||
from .parks_media import (
|
||||
ParkPhotoOutputSerializer,
|
||||
ParkPhotoCreateInputSerializer,
|
||||
ParkPhotoUpdateInputSerializer,
|
||||
ParkPhotoListOutputSerializer,
|
||||
ParkPhotoApprovalInputSerializer,
|
||||
ParkPhotoStatsOutputSerializer,
|
||||
)
|
||||
|
||||
# Rides media domain
|
||||
from .rides_media import (
|
||||
RidePhotoOutputSerializer,
|
||||
RidePhotoCreateInputSerializer,
|
||||
RidePhotoUpdateInputSerializer,
|
||||
RidePhotoListOutputSerializer,
|
||||
RidePhotoApprovalInputSerializer,
|
||||
RidePhotoStatsOutputSerializer,
|
||||
RidePhotoTypeFilterSerializer,
|
||||
)
|
||||
|
||||
# Search domain
|
||||
from .search import (
|
||||
EntitySearchInputSerializer,
|
||||
EntitySearchResultSerializer,
|
||||
EntitySearchOutputSerializer,
|
||||
LocationSearchResultSerializer,
|
||||
LocationSearchOutputSerializer,
|
||||
ReverseGeocodeOutputSerializer,
|
||||
)
|
||||
|
||||
# History domain
|
||||
from .history import (
|
||||
ParkHistoryEventSerializer,
|
||||
RideHistoryEventSerializer,
|
||||
ParkHistoryOutputSerializer,
|
||||
RideHistoryOutputSerializer,
|
||||
UnifiedHistoryTimelineSerializer,
|
||||
HistorySummarySerializer,
|
||||
)
|
||||
|
||||
# Services domain
|
||||
from .services import (
|
||||
EmailSendInputSerializer,
|
||||
EmailTemplateOutputSerializer,
|
||||
MapDataOutputSerializer,
|
||||
CoordinateInputSerializer,
|
||||
HistoryEventSerializer,
|
||||
HistoryEntryOutputSerializer,
|
||||
HistoryCreateInputSerializer,
|
||||
ModerationSubmissionSerializer,
|
||||
ModerationSubmissionOutputSerializer,
|
||||
RoadtripParkSerializer,
|
||||
RoadtripCreateInputSerializer,
|
||||
RoadtripOutputSerializer,
|
||||
GeocodeInputSerializer,
|
||||
GeocodeOutputSerializer,
|
||||
DistanceCalculationInputSerializer,
|
||||
DistanceCalculationOutputSerializer,
|
||||
)
|
||||
|
||||
# Re-export everything for backward compatibility
|
||||
__all__ = [
|
||||
# Shared
|
||||
"CATEGORY_CHOICES",
|
||||
"ModelChoices",
|
||||
"LocationOutputSerializer",
|
||||
"CompanyOutputSerializer",
|
||||
"UserModel",
|
||||
# Parks
|
||||
"ParkListOutputSerializer",
|
||||
"ParkDetailOutputSerializer",
|
||||
"ParkCreateInputSerializer",
|
||||
"ParkUpdateInputSerializer",
|
||||
"ParkFilterInputSerializer",
|
||||
"ParkAreaDetailOutputSerializer",
|
||||
"ParkAreaCreateInputSerializer",
|
||||
"ParkAreaUpdateInputSerializer",
|
||||
"ParkLocationOutputSerializer",
|
||||
"ParkLocationCreateInputSerializer",
|
||||
"ParkLocationUpdateInputSerializer",
|
||||
"ParkSuggestionSerializer",
|
||||
"ParkSuggestionOutputSerializer",
|
||||
# Companies
|
||||
"CompanyDetailOutputSerializer",
|
||||
"CompanyCreateInputSerializer",
|
||||
"CompanyUpdateInputSerializer",
|
||||
"RideModelDetailOutputSerializer",
|
||||
"RideModelCreateInputSerializer",
|
||||
"RideModelUpdateInputSerializer",
|
||||
# Rides
|
||||
"RideParkOutputSerializer",
|
||||
"RideModelOutputSerializer",
|
||||
"RideListOutputSerializer",
|
||||
"RideDetailOutputSerializer",
|
||||
"RideCreateInputSerializer",
|
||||
"RideUpdateInputSerializer",
|
||||
"RideFilterInputSerializer",
|
||||
"RollerCoasterStatsOutputSerializer",
|
||||
"RollerCoasterStatsCreateInputSerializer",
|
||||
"RollerCoasterStatsUpdateInputSerializer",
|
||||
"RideLocationOutputSerializer",
|
||||
"RideLocationCreateInputSerializer",
|
||||
"RideLocationUpdateInputSerializer",
|
||||
"RideReviewOutputSerializer",
|
||||
"RideReviewCreateInputSerializer",
|
||||
"RideReviewUpdateInputSerializer",
|
||||
# Services
|
||||
"EmailSendInputSerializer",
|
||||
"EmailTemplateOutputSerializer",
|
||||
"MapDataOutputSerializer",
|
||||
"CoordinateInputSerializer",
|
||||
"HistoryEventSerializer",
|
||||
"HistoryEntryOutputSerializer",
|
||||
"HistoryCreateInputSerializer",
|
||||
"ModerationSubmissionSerializer",
|
||||
"ModerationSubmissionOutputSerializer",
|
||||
"RoadtripParkSerializer",
|
||||
"RoadtripCreateInputSerializer",
|
||||
"RoadtripOutputSerializer",
|
||||
"GeocodeInputSerializer",
|
||||
"GeocodeOutputSerializer",
|
||||
"DistanceCalculationInputSerializer",
|
||||
"DistanceCalculationOutputSerializer",
|
||||
# Media
|
||||
"PhotoUploadInputSerializer",
|
||||
"PhotoDetailOutputSerializer",
|
||||
"PhotoListOutputSerializer",
|
||||
"PhotoUpdateInputSerializer",
|
||||
# Parks Media
|
||||
"ParkPhotoOutputSerializer",
|
||||
"ParkPhotoCreateInputSerializer",
|
||||
"ParkPhotoUpdateInputSerializer",
|
||||
"ParkPhotoListOutputSerializer",
|
||||
"ParkPhotoApprovalInputSerializer",
|
||||
"ParkPhotoStatsOutputSerializer",
|
||||
# Rides Media
|
||||
"RidePhotoOutputSerializer",
|
||||
"RidePhotoCreateInputSerializer",
|
||||
"RidePhotoUpdateInputSerializer",
|
||||
"RidePhotoListOutputSerializer",
|
||||
"RidePhotoApprovalInputSerializer",
|
||||
"RidePhotoStatsOutputSerializer",
|
||||
"RidePhotoTypeFilterSerializer",
|
||||
# Search
|
||||
"EntitySearchInputSerializer",
|
||||
"EntitySearchResultSerializer",
|
||||
"EntitySearchOutputSerializer",
|
||||
"LocationSearchResultSerializer",
|
||||
"LocationSearchOutputSerializer",
|
||||
"ReverseGeocodeOutputSerializer",
|
||||
# History
|
||||
"ParkHistoryEventSerializer",
|
||||
"RideHistoryEventSerializer",
|
||||
"ParkHistoryOutputSerializer",
|
||||
"RideHistoryOutputSerializer",
|
||||
"UnifiedHistoryTimelineSerializer",
|
||||
"HistorySummarySerializer",
|
||||
# Statistics and health
|
||||
"ParkStatsOutputSerializer",
|
||||
"RideStatsOutputSerializer",
|
||||
"ParkReviewOutputSerializer",
|
||||
"HealthCheckOutputSerializer",
|
||||
"PerformanceMetricsOutputSerializer",
|
||||
"SimpleHealthOutputSerializer",
|
||||
# Accounts
|
||||
# --- Accounts domain: try multiple likely locations, fall back to placeholders ---
|
||||
_ACCOUNTS_SYMBOLS: List[str] = [
|
||||
"UserProfileOutputSerializer",
|
||||
"UserProfileCreateInputSerializer",
|
||||
"UserProfileUpdateInputSerializer",
|
||||
@@ -292,3 +109,168 @@ __all__ = [
|
||||
"SocialProviderOutputSerializer",
|
||||
"AuthStatusOutputSerializer",
|
||||
]
|
||||
|
||||
|
||||
def _import_accounts_symbols() -> Dict[str, Any]:
|
||||
"""
|
||||
Try a list of candidate module paths and return a dict mapping expected symbol
|
||||
names to the objects found. If no candidate provides a symbol, the symbol maps to None.
|
||||
"""
|
||||
candidates = [
|
||||
f"{__package__}.accounts",
|
||||
f"{__package__}.auth",
|
||||
"apps.accounts.serializers",
|
||||
"apps.api.v1.auth.serializers",
|
||||
]
|
||||
|
||||
# Prepare default placeholders
|
||||
result: Dict[str, Any] = {name: None for name in _ACCOUNTS_SYMBOLS}
|
||||
|
||||
for modname in candidates:
|
||||
try:
|
||||
module = importlib.import_module(modname)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Fill in any symbols that exist on this module (don't require all)
|
||||
for name in _ACCOUNTS_SYMBOLS:
|
||||
if hasattr(module, name):
|
||||
result[name] = getattr(module, name)
|
||||
|
||||
# If we've found at least one real object (not all None), stop trying further candidates.
|
||||
if any(result[name] is not None for name in _ACCOUNTS_SYMBOLS):
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
|
||||
_accounts = _import_accounts_symbols()
|
||||
|
||||
# Bind account symbols into the module namespace (either actual objects or None)
|
||||
for _name in _ACCOUNTS_SYMBOLS:
|
||||
globals()[_name] = _accounts.get(_name)
|
||||
|
||||
# --- Services domain ---
|
||||
|
||||
# --- Optionally try importing other domain modules and inject serializer-like names ---
|
||||
_optional_domains = [
|
||||
"other",
|
||||
"media",
|
||||
"parks_media",
|
||||
"rides_media",
|
||||
"search",
|
||||
"history",
|
||||
]
|
||||
for domain in _optional_domains:
|
||||
modname = f"{__package__}.{domain}"
|
||||
try:
|
||||
module = importlib.import_module(modname)
|
||||
except Exception:
|
||||
continue
|
||||
# Inject any attribute that looks like a serializer or matches uppercase naming used by exported symbols
|
||||
for attr in dir(module):
|
||||
if attr.startswith("_"):
|
||||
continue
|
||||
# Heuristic: export classes/constants that end with 'Serializer' or are uppercase constants
|
||||
if (
|
||||
attr.endswith("Serializer")
|
||||
or attr.isupper()
|
||||
or attr.endswith("OutputSerializer")
|
||||
or attr.endswith("InputSerializer")
|
||||
):
|
||||
globals()[attr] = getattr(module, attr)
|
||||
|
||||
# --- Construct a conservative __all__ based on explicit lists and discovered serializer names ---
|
||||
_SHARED_EXPORTS = [
|
||||
"CATEGORY_CHOICES",
|
||||
"ModelChoices",
|
||||
"LocationOutputSerializer",
|
||||
"CompanyOutputSerializer",
|
||||
"UserModel",
|
||||
]
|
||||
|
||||
_PARKS_EXPORTS = [
|
||||
"ParkListOutputSerializer",
|
||||
"ParkDetailOutputSerializer",
|
||||
"ParkCreateInputSerializer",
|
||||
"ParkUpdateInputSerializer",
|
||||
"ParkFilterInputSerializer",
|
||||
"ParkAreaDetailOutputSerializer",
|
||||
"ParkAreaCreateInputSerializer",
|
||||
"ParkAreaUpdateInputSerializer",
|
||||
"ParkLocationOutputSerializer",
|
||||
"ParkLocationCreateInputSerializer",
|
||||
"ParkLocationUpdateInputSerializer",
|
||||
"ParkSuggestionSerializer",
|
||||
"ParkSuggestionOutputSerializer",
|
||||
]
|
||||
|
||||
_COMPANIES_EXPORTS = [
|
||||
"CompanyDetailOutputSerializer",
|
||||
"CompanyCreateInputSerializer",
|
||||
"CompanyUpdateInputSerializer",
|
||||
"RideModelDetailOutputSerializer",
|
||||
"RideModelCreateInputSerializer",
|
||||
"RideModelUpdateInputSerializer",
|
||||
]
|
||||
|
||||
_RIDES_EXPORTS = [
|
||||
"RideParkOutputSerializer",
|
||||
"RideModelOutputSerializer",
|
||||
"RideListOutputSerializer",
|
||||
"RideDetailOutputSerializer",
|
||||
"RideCreateInputSerializer",
|
||||
"RideUpdateInputSerializer",
|
||||
"RideFilterInputSerializer",
|
||||
"RollerCoasterStatsOutputSerializer",
|
||||
"RollerCoasterStatsCreateInputSerializer",
|
||||
"RollerCoasterStatsUpdateInputSerializer",
|
||||
"RideLocationOutputSerializer",
|
||||
"RideLocationCreateInputSerializer",
|
||||
"RideLocationUpdateInputSerializer",
|
||||
"RideReviewOutputSerializer",
|
||||
"RideReviewCreateInputSerializer",
|
||||
"RideReviewUpdateInputSerializer",
|
||||
]
|
||||
|
||||
_SERVICES_EXPORTS = [
|
||||
"HealthCheckOutputSerializer",
|
||||
"PerformanceMetricsOutputSerializer",
|
||||
"SimpleHealthOutputSerializer",
|
||||
"EmailSendInputSerializer",
|
||||
"EmailTemplateOutputSerializer",
|
||||
"MapDataOutputSerializer",
|
||||
"CoordinateInputSerializer",
|
||||
"HistoryEventSerializer",
|
||||
"HistoryEntryOutputSerializer",
|
||||
"HistoryCreateInputSerializer",
|
||||
"ModerationSubmissionSerializer",
|
||||
"ModerationSubmissionOutputSerializer",
|
||||
"RoadtripParkSerializer",
|
||||
"RoadtripCreateInputSerializer",
|
||||
"RoadtripOutputSerializer",
|
||||
"GeocodeInputSerializer",
|
||||
"GeocodeOutputSerializer",
|
||||
"DistanceCalculationInputSerializer",
|
||||
"DistanceCalculationOutputSerializer",
|
||||
]
|
||||
|
||||
# Build __all__ from known exports plus any serializer-like names discovered above
|
||||
__all__ = (
|
||||
_SHARED_EXPORTS
|
||||
+ _PARKS_EXPORTS
|
||||
+ _COMPANIES_EXPORTS
|
||||
+ _RIDES_EXPORTS
|
||||
+ _SERVICES_EXPORTS
|
||||
+ _ACCOUNTS_SYMBOLS
|
||||
)
|
||||
|
||||
# Add any discovered globals that look like serializers (avoid duplicates)
|
||||
for name in list(globals().keys()):
|
||||
if name in __all__:
|
||||
continue
|
||||
if name.endswith(("Serializer", "OutputSerializer", "InputSerializer")):
|
||||
__all__.append(name)
|
||||
|
||||
# Ensure __all__ is a flat list of unique strings (preserve order)
|
||||
__all__ = list(dict.fromkeys(__all__))
|
||||
|
||||
497
backend/apps/api/v1/serializers/auth.py
Normal file
497
backend/apps/api/v1/serializers/auth.py
Normal file
@@ -0,0 +1,497 @@
|
||||
"""
|
||||
Authentication domain serializers for ThrillWiki API v1.
|
||||
|
||||
This module contains all serializers related to user authentication,
|
||||
registration, password management, and social authentication.
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model, authenticate
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from drf_spectacular.utils import (
|
||||
extend_schema_serializer,
|
||||
OpenApiExample,
|
||||
)
|
||||
|
||||
UserModel = get_user_model()
|
||||
|
||||
|
||||
# === USER SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"User Output Example",
|
||||
summary="Example user response",
|
||||
description="A typical user object in API responses",
|
||||
value={
|
||||
"id": 1,
|
||||
"username": "thrillseeker",
|
||||
"email": "user@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"is_active": True,
|
||||
"date_joined": "2024-01-01T00:00:00Z",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class UserOutputSerializer(serializers.ModelSerializer):
|
||||
"""Output serializer for user data."""
|
||||
|
||||
class Meta:
|
||||
model = UserModel
|
||||
fields = [
|
||||
"id",
|
||||
"username",
|
||||
"email",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"is_active",
|
||||
"date_joined",
|
||||
]
|
||||
read_only_fields = ["id", "date_joined"]
|
||||
|
||||
|
||||
# === LOGIN SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Login Input Example",
|
||||
summary="Example login request",
|
||||
description="Login with username or email and password",
|
||||
value={
|
||||
"username": "thrillseeker",
|
||||
"password": "securepassword123",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class LoginInputSerializer(serializers.Serializer):
|
||||
"""Input serializer for user login."""
|
||||
|
||||
username = serializers.CharField(
|
||||
max_length=150,
|
||||
help_text="Username or email address",
|
||||
)
|
||||
password = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="User password",
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Validate login credentials."""
|
||||
username = attrs.get("username")
|
||||
password = attrs.get("password")
|
||||
|
||||
if username and password:
|
||||
# Try to authenticate with the provided credentials
|
||||
user = authenticate(
|
||||
request=self.context.get("request"),
|
||||
username=username,
|
||||
password=password,
|
||||
)
|
||||
|
||||
if not user:
|
||||
# Try email-based authentication if username failed
|
||||
if "@" in username:
|
||||
try:
|
||||
user_obj = UserModel.objects.get(email=username)
|
||||
user = authenticate(
|
||||
request=self.context.get("request"),
|
||||
username=user_obj.username, # type: ignore[attr-defined]
|
||||
password=password,
|
||||
)
|
||||
except UserModel.DoesNotExist:
|
||||
pass
|
||||
|
||||
if not user:
|
||||
raise serializers.ValidationError("Invalid credentials")
|
||||
|
||||
if not user.is_active:
|
||||
raise serializers.ValidationError("Account is disabled")
|
||||
|
||||
attrs["user"] = user
|
||||
else:
|
||||
raise serializers.ValidationError("Must include username and password")
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Login Output Example",
|
||||
summary="Example login response",
|
||||
description="Successful login response with token and user data",
|
||||
value={
|
||||
"token": "abc123def456ghi789",
|
||||
"user": {
|
||||
"id": 1,
|
||||
"username": "thrillseeker",
|
||||
"email": "user@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
},
|
||||
"message": "Login successful",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class LoginOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for login response."""
|
||||
|
||||
token = serializers.CharField(help_text="Authentication token")
|
||||
user = UserOutputSerializer(help_text="User information")
|
||||
message = serializers.CharField(help_text="Success message")
|
||||
|
||||
|
||||
# === SIGNUP SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Signup Input Example",
|
||||
summary="Example registration request",
|
||||
description="Register a new user account",
|
||||
value={
|
||||
"username": "newuser",
|
||||
"email": "newuser@example.com",
|
||||
"password": "securepassword123",
|
||||
"password_confirm": "securepassword123",
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class SignupInputSerializer(serializers.ModelSerializer):
|
||||
"""Input serializer for user registration."""
|
||||
|
||||
password = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="User password",
|
||||
)
|
||||
password_confirm = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="Password confirmation",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = UserModel
|
||||
fields = [
|
||||
"username",
|
||||
"email",
|
||||
"password",
|
||||
"password_confirm",
|
||||
"first_name",
|
||||
"last_name",
|
||||
]
|
||||
|
||||
def validate_email(self, value):
|
||||
"""Validate email uniqueness."""
|
||||
if UserModel.objects.filter(email=value).exists():
|
||||
raise serializers.ValidationError("Email already registered")
|
||||
return value
|
||||
|
||||
def validate_username(self, value):
|
||||
"""Validate username uniqueness."""
|
||||
if UserModel.objects.filter(username=value).exists():
|
||||
raise serializers.ValidationError("Username already taken")
|
||||
return value
|
||||
|
||||
def validate_password(self, value):
|
||||
"""Validate password strength."""
|
||||
try:
|
||||
validate_password(value)
|
||||
except DjangoValidationError as e:
|
||||
raise serializers.ValidationError(list(e.messages))
|
||||
return value
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Cross-field validation."""
|
||||
password = attrs.get("password")
|
||||
password_confirm = attrs.get("password_confirm")
|
||||
|
||||
if password != password_confirm:
|
||||
raise serializers.ValidationError("Passwords do not match")
|
||||
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create new user."""
|
||||
validated_data.pop("password_confirm")
|
||||
password = validated_data.pop("password")
|
||||
|
||||
user = UserModel.objects.create_user( # type: ignore[attr-defined]
|
||||
password=password,
|
||||
**validated_data,
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Signup Output Example",
|
||||
summary="Example registration response",
|
||||
description="Successful registration response with token and user data",
|
||||
value={
|
||||
"token": "abc123def456ghi789",
|
||||
"user": {
|
||||
"id": 2,
|
||||
"username": "newuser",
|
||||
"email": "newuser@example.com",
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith",
|
||||
},
|
||||
"message": "Registration successful",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class SignupOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for registration response."""
|
||||
|
||||
token = serializers.CharField(help_text="Authentication token")
|
||||
user = UserOutputSerializer(help_text="User information")
|
||||
message = serializers.CharField(help_text="Success message")
|
||||
|
||||
|
||||
# === LOGOUT SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Logout Output Example",
|
||||
summary="Example logout response",
|
||||
description="Successful logout response",
|
||||
value={
|
||||
"message": "Logout successful",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class LogoutOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for logout response."""
|
||||
|
||||
message = serializers.CharField(help_text="Success message")
|
||||
|
||||
|
||||
# === PASSWORD RESET SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Password Reset Input Example",
|
||||
summary="Example password reset request",
|
||||
description="Request password reset email",
|
||||
value={
|
||||
"email": "user@example.com",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class PasswordResetInputSerializer(serializers.Serializer):
|
||||
"""Input serializer for password reset request."""
|
||||
|
||||
email = serializers.EmailField(help_text="Email address for password reset")
|
||||
|
||||
def validate_email(self, value):
|
||||
"""Validate email exists."""
|
||||
if not UserModel.objects.filter(email=value).exists():
|
||||
# Don't reveal if email exists for security
|
||||
pass
|
||||
return value
|
||||
|
||||
def save(self, **kwargs): # type: ignore[override]
|
||||
"""Send password reset email."""
|
||||
email = self.validated_data["email"] # type: ignore[index]
|
||||
try:
|
||||
_user = UserModel.objects.get(email=email)
|
||||
# Here you would typically send a password reset email
|
||||
# For now, we'll just pass
|
||||
pass
|
||||
except UserModel.DoesNotExist:
|
||||
# Don't reveal if email exists for security
|
||||
pass
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Password Reset Output Example",
|
||||
summary="Example password reset response",
|
||||
description="Password reset email sent response",
|
||||
value={
|
||||
"detail": "Password reset email sent",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class PasswordResetOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for password reset response."""
|
||||
|
||||
detail = serializers.CharField(help_text="Success message")
|
||||
|
||||
|
||||
# === PASSWORD CHANGE SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Password Change Input Example",
|
||||
summary="Example password change request",
|
||||
description="Change current user's password",
|
||||
value={
|
||||
"old_password": "oldpassword123",
|
||||
"new_password": "newpassword456",
|
||||
"new_password_confirm": "newpassword456",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class PasswordChangeInputSerializer(serializers.Serializer):
|
||||
"""Input serializer for password change."""
|
||||
|
||||
old_password = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="Current password",
|
||||
)
|
||||
new_password = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="New password",
|
||||
)
|
||||
new_password_confirm = serializers.CharField(
|
||||
write_only=True,
|
||||
style={"input_type": "password"},
|
||||
help_text="New password confirmation",
|
||||
)
|
||||
|
||||
def validate_old_password(self, value):
|
||||
"""Validate current password."""
|
||||
user = self.context["request"].user
|
||||
if not user.check_password(value):
|
||||
raise serializers.ValidationError("Current password is incorrect")
|
||||
return value
|
||||
|
||||
def validate_new_password(self, value):
|
||||
"""Validate new password strength."""
|
||||
try:
|
||||
validate_password(value, user=self.context["request"].user)
|
||||
except DjangoValidationError as e:
|
||||
raise serializers.ValidationError(list(e.messages))
|
||||
return value
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Cross-field validation."""
|
||||
new_password = attrs.get("new_password")
|
||||
new_password_confirm = attrs.get("new_password_confirm")
|
||||
|
||||
if new_password != new_password_confirm:
|
||||
raise serializers.ValidationError("New passwords do not match")
|
||||
|
||||
return attrs
|
||||
|
||||
def save(self, **kwargs): # type: ignore[override]
|
||||
"""Change user password."""
|
||||
user = self.context["request"].user
|
||||
user.set_password(self.validated_data["new_password"]) # type: ignore[index]
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Password Change Output Example",
|
||||
summary="Example password change response",
|
||||
description="Password changed successfully response",
|
||||
value={
|
||||
"detail": "Password changed successfully",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class PasswordChangeOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for password change response."""
|
||||
|
||||
detail = serializers.CharField(help_text="Success message")
|
||||
|
||||
|
||||
# === SOCIAL PROVIDER SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Social Provider Example",
|
||||
summary="Example social provider",
|
||||
description="Available social authentication provider",
|
||||
value={
|
||||
"id": "google",
|
||||
"name": "Google",
|
||||
"authUrl": "https://example.com/accounts/google/login/",
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class SocialProviderOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for social authentication providers."""
|
||||
|
||||
id = serializers.CharField(help_text="Provider ID")
|
||||
name = serializers.CharField(help_text="Provider display name")
|
||||
authUrl = serializers.URLField(help_text="Authentication URL")
|
||||
|
||||
|
||||
# === AUTH STATUS SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Auth Status Authenticated Example",
|
||||
summary="Example authenticated status",
|
||||
description="Response when user is authenticated",
|
||||
value={
|
||||
"authenticated": True,
|
||||
"user": {
|
||||
"id": 1,
|
||||
"username": "thrillseeker",
|
||||
"email": "user@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
},
|
||||
},
|
||||
),
|
||||
OpenApiExample(
|
||||
"Auth Status Unauthenticated Example",
|
||||
summary="Example unauthenticated status",
|
||||
description="Response when user is not authenticated",
|
||||
value={
|
||||
"authenticated": False,
|
||||
"user": None,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
class AuthStatusOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for authentication status."""
|
||||
|
||||
authenticated = serializers.BooleanField(help_text="Whether user is authenticated")
|
||||
user = UserOutputSerializer(
|
||||
allow_null=True, help_text="User information if authenticated"
|
||||
)
|
||||
@@ -38,8 +38,10 @@ class EntitySearchResultSerializer(serializers.Serializer):
|
||||
description = serializers.CharField()
|
||||
relevance_score = serializers.FloatField()
|
||||
|
||||
# Context-specific info
|
||||
context = serializers.JSONField(help_text="Additional context based on entity type")
|
||||
# Context-specific info — renamed to avoid overriding Serializer.context
|
||||
context_info = serializers.JSONField(
|
||||
help_text="Additional context based on entity type"
|
||||
)
|
||||
|
||||
|
||||
class EntitySearchOutputSerializer(serializers.Serializer):
|
||||
|
||||
@@ -11,6 +11,40 @@ from drf_spectacular.utils import (
|
||||
)
|
||||
|
||||
|
||||
# === HEALTH CHECK SERIALIZERS ===
|
||||
|
||||
|
||||
class HealthCheckOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for comprehensive health check responses."""
|
||||
|
||||
status = serializers.CharField(help_text="Overall health status")
|
||||
timestamp = serializers.DateTimeField(help_text="Timestamp of health check")
|
||||
version = serializers.CharField(help_text="Application version")
|
||||
environment = serializers.CharField(help_text="Environment name")
|
||||
response_time_ms = serializers.FloatField(help_text="Response time in milliseconds")
|
||||
checks = serializers.DictField(help_text="Individual health check results")
|
||||
metrics = serializers.DictField(help_text="System metrics")
|
||||
|
||||
|
||||
class PerformanceMetricsOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for performance metrics responses."""
|
||||
|
||||
timestamp = serializers.DateTimeField(help_text="Timestamp of metrics collection")
|
||||
database_analysis = serializers.DictField(help_text="Database performance analysis")
|
||||
cache_performance = serializers.DictField(help_text="Cache performance metrics")
|
||||
recent_slow_queries = serializers.DictField(help_text="Recent slow query analysis")
|
||||
|
||||
|
||||
class SimpleHealthOutputSerializer(serializers.Serializer):
|
||||
"""Output serializer for simple health check responses."""
|
||||
|
||||
status = serializers.CharField(help_text="Simple health status")
|
||||
timestamp = serializers.DateTimeField(help_text="Timestamp of health check")
|
||||
error = serializers.CharField(
|
||||
required=False, help_text="Error message if unhealthy"
|
||||
)
|
||||
|
||||
|
||||
# === EMAIL SERVICE SERIALIZERS ===
|
||||
|
||||
|
||||
@@ -22,7 +56,7 @@ class EmailSendInputSerializer(serializers.Serializer):
|
||||
text = serializers.CharField()
|
||||
html = serializers.CharField(required=False)
|
||||
template = serializers.CharField(required=False)
|
||||
context = serializers.JSONField(required=False)
|
||||
template_context = serializers.JSONField(required=False)
|
||||
|
||||
|
||||
class EmailTemplateOutputSerializer(serializers.Serializer):
|
||||
@@ -210,11 +244,11 @@ class DistanceCalculationInputSerializer(serializers.Serializer):
|
||||
park1_id = serializers.IntegerField(help_text="ID of first park")
|
||||
park2_id = serializers.IntegerField(help_text="ID of second park")
|
||||
|
||||
def validate(self, data):
|
||||
def validate(self, attrs):
|
||||
"""Validate that park IDs are different."""
|
||||
if data["park1_id"] == data["park2_id"]:
|
||||
if attrs["park1_id"] == attrs["park2_id"]:
|
||||
raise serializers.ValidationError("Park IDs must be different")
|
||||
return data
|
||||
return attrs
|
||||
|
||||
|
||||
class DistanceCalculationOutputSerializer(serializers.Serializer):
|
||||
|
||||
Reference in New Issue
Block a user