mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 08:31:08 -05:00
- Add complete backend/ directory with full Django application - Add frontend/ directory with Vite + TypeScript setup ready for Next.js - Add comprehensive shared/ directory with: - Complete documentation and memory-bank archives - Media files and avatars (letters, park/ride images) - Deployment scripts and automation tools - Shared types and utilities - Add architecture/ directory with migration guides - Configure pnpm workspace for monorepo development - Update .gitignore to exclude .django_tailwind_cli/ build artifacts - Preserve all historical documentation in shared/docs/memory-bank/ - Set up proper structure for full-stack development with shared resources
305 lines
9.4 KiB
Python
305 lines
9.4 KiB
Python
"""
|
|
Serializers for Parks API following Django styleguide patterns.
|
|
Separates Input and Output serializers for clear boundaries.
|
|
"""
|
|
|
|
from rest_framework import serializers
|
|
from ..models import Park
|
|
|
|
|
|
class ParkLocationOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park location data."""
|
|
|
|
latitude = serializers.SerializerMethodField()
|
|
longitude = serializers.SerializerMethodField()
|
|
city = serializers.SerializerMethodField()
|
|
state = serializers.SerializerMethodField()
|
|
country = serializers.SerializerMethodField()
|
|
formatted_address = serializers.SerializerMethodField()
|
|
|
|
def get_latitude(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.latitude
|
|
return None
|
|
|
|
def get_longitude(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.longitude
|
|
return None
|
|
|
|
def get_city(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.city
|
|
return None
|
|
|
|
def get_state(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.state
|
|
return None
|
|
|
|
def get_country(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.country
|
|
return None
|
|
|
|
def get_formatted_address(self, obj):
|
|
if hasattr(obj, "location") and obj.location:
|
|
return obj.location.formatted_address
|
|
return ""
|
|
|
|
|
|
class CompanyOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for company data."""
|
|
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
roles = serializers.ListField(child=serializers.CharField())
|
|
|
|
|
|
class ParkAreaOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park area data."""
|
|
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
description = serializers.CharField()
|
|
|
|
|
|
class ParkListOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park list view."""
|
|
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
status = serializers.CharField()
|
|
description = serializers.CharField()
|
|
|
|
# Statistics
|
|
average_rating = serializers.DecimalField(
|
|
max_digits=3, decimal_places=2, allow_null=True
|
|
)
|
|
coaster_count = serializers.IntegerField(allow_null=True)
|
|
ride_count = serializers.IntegerField(allow_null=True)
|
|
|
|
# Location (simplified for list view)
|
|
location = ParkLocationOutputSerializer(allow_null=True)
|
|
|
|
# Operator info
|
|
operator = CompanyOutputSerializer()
|
|
|
|
# Metadata
|
|
created_at = serializers.DateTimeField()
|
|
updated_at = serializers.DateTimeField()
|
|
|
|
|
|
class ParkDetailOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park detail view."""
|
|
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
status = serializers.CharField()
|
|
description = serializers.CharField()
|
|
|
|
# Details
|
|
opening_date = serializers.DateField(allow_null=True)
|
|
closing_date = serializers.DateField(allow_null=True)
|
|
operating_season = serializers.CharField()
|
|
size_acres = serializers.DecimalField(
|
|
max_digits=10, decimal_places=2, allow_null=True
|
|
)
|
|
website = serializers.URLField()
|
|
|
|
# Statistics
|
|
average_rating = serializers.DecimalField(
|
|
max_digits=3, decimal_places=2, allow_null=True
|
|
)
|
|
coaster_count = serializers.IntegerField(allow_null=True)
|
|
ride_count = serializers.IntegerField(allow_null=True)
|
|
|
|
# Location (full details)
|
|
location = ParkLocationOutputSerializer(allow_null=True)
|
|
|
|
# Companies
|
|
operator = CompanyOutputSerializer()
|
|
property_owner = CompanyOutputSerializer(allow_null=True)
|
|
|
|
# Areas
|
|
areas = ParkAreaOutputSerializer(many=True)
|
|
|
|
# Metadata
|
|
created_at = serializers.DateTimeField()
|
|
updated_at = serializers.DateTimeField()
|
|
|
|
|
|
class ParkCreateInputSerializer(serializers.Serializer):
|
|
"""Input serializer for creating parks."""
|
|
|
|
name = serializers.CharField(max_length=255)
|
|
description = serializers.CharField(allow_blank=True, default="")
|
|
status = serializers.ChoiceField(choices=Park.STATUS_CHOICES, default="OPERATING")
|
|
|
|
# Optional details
|
|
opening_date = serializers.DateField(required=False, allow_null=True)
|
|
closing_date = serializers.DateField(required=False, allow_null=True)
|
|
operating_season = serializers.CharField(
|
|
max_length=255, required=False, allow_blank=True
|
|
)
|
|
size_acres = serializers.DecimalField(
|
|
max_digits=10, decimal_places=2, required=False, allow_null=True
|
|
)
|
|
website = serializers.URLField(required=False, allow_blank=True)
|
|
|
|
# Required operator
|
|
operator_id = serializers.IntegerField()
|
|
|
|
# Optional property owner
|
|
property_owner_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
def validate(self, data):
|
|
"""Cross-field validation."""
|
|
opening_date = data.get("opening_date")
|
|
closing_date = data.get("closing_date")
|
|
|
|
if opening_date and closing_date and closing_date < opening_date:
|
|
raise serializers.ValidationError(
|
|
"Closing date cannot be before opening date"
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class ParkUpdateInputSerializer(serializers.Serializer):
|
|
"""Input serializer for updating parks."""
|
|
|
|
name = serializers.CharField(max_length=255, required=False)
|
|
description = serializers.CharField(allow_blank=True, required=False)
|
|
status = serializers.ChoiceField(choices=Park.STATUS_CHOICES, required=False)
|
|
|
|
# Optional details
|
|
opening_date = serializers.DateField(required=False, allow_null=True)
|
|
closing_date = serializers.DateField(required=False, allow_null=True)
|
|
operating_season = serializers.CharField(
|
|
max_length=255, required=False, allow_blank=True
|
|
)
|
|
size_acres = serializers.DecimalField(
|
|
max_digits=10, decimal_places=2, required=False, allow_null=True
|
|
)
|
|
website = serializers.URLField(required=False, allow_blank=True)
|
|
|
|
# Companies
|
|
operator_id = serializers.IntegerField(required=False)
|
|
property_owner_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
def validate(self, data):
|
|
"""Cross-field validation."""
|
|
opening_date = data.get("opening_date")
|
|
closing_date = data.get("closing_date")
|
|
|
|
if opening_date and closing_date and closing_date < opening_date:
|
|
raise serializers.ValidationError(
|
|
"Closing date cannot be before opening date"
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class ParkFilterInputSerializer(serializers.Serializer):
|
|
"""Input serializer for park filtering and search."""
|
|
|
|
# Search
|
|
search = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
# Status filter
|
|
status = serializers.MultipleChoiceField(
|
|
choices=Park.STATUS_CHOICES, required=False
|
|
)
|
|
|
|
# Location filters
|
|
country = serializers.CharField(required=False, allow_blank=True)
|
|
state = serializers.CharField(required=False, allow_blank=True)
|
|
city = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
# Rating filter
|
|
min_rating = serializers.DecimalField(
|
|
max_digits=3,
|
|
decimal_places=2,
|
|
required=False,
|
|
min_value=1,
|
|
max_value=10,
|
|
)
|
|
|
|
# Size filter
|
|
min_size_acres = serializers.DecimalField(
|
|
max_digits=10, decimal_places=2, required=False, min_value=0
|
|
)
|
|
max_size_acres = serializers.DecimalField(
|
|
max_digits=10, decimal_places=2, required=False, min_value=0
|
|
)
|
|
|
|
# Company filters
|
|
operator_id = serializers.IntegerField(required=False)
|
|
property_owner_id = serializers.IntegerField(required=False)
|
|
|
|
# Ordering
|
|
ordering = serializers.ChoiceField(
|
|
choices=[
|
|
"name",
|
|
"-name",
|
|
"opening_date",
|
|
"-opening_date",
|
|
"average_rating",
|
|
"-average_rating",
|
|
"coaster_count",
|
|
"-coaster_count",
|
|
"created_at",
|
|
"-created_at",
|
|
],
|
|
required=False,
|
|
default="name",
|
|
)
|
|
|
|
|
|
class ParkReviewOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park reviews."""
|
|
|
|
id = serializers.IntegerField()
|
|
rating = serializers.IntegerField()
|
|
title = serializers.CharField()
|
|
content = serializers.CharField()
|
|
visit_date = serializers.DateField()
|
|
created_at = serializers.DateTimeField()
|
|
|
|
# User info (limited for privacy)
|
|
user = serializers.SerializerMethodField()
|
|
|
|
def get_user(self, obj):
|
|
return {
|
|
"username": obj.user.username,
|
|
"display_name": obj.user.get_full_name() or obj.user.username,
|
|
}
|
|
|
|
|
|
class ParkStatsOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for park statistics."""
|
|
|
|
total_parks = serializers.IntegerField()
|
|
operating_parks = serializers.IntegerField()
|
|
closed_parks = serializers.IntegerField()
|
|
under_construction = serializers.IntegerField()
|
|
|
|
# Averages
|
|
average_rating = serializers.DecimalField(
|
|
max_digits=3, decimal_places=2, allow_null=True
|
|
)
|
|
average_coaster_count = serializers.DecimalField(
|
|
max_digits=5, decimal_places=2, allow_null=True
|
|
)
|
|
|
|
# Top countries
|
|
top_countries = serializers.ListField(child=serializers.DictField())
|
|
|
|
# Recently added
|
|
recently_added_count = serializers.IntegerField()
|