mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-21 08:11:08 -05:00
- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks. - Added tests for filtering, searching, and ordering parks in the API. - Created tests for error handling in the API, including malformed JSON and unsupported methods. - Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced. - Introduced utility mixins for API and model testing to streamline assertions and enhance test readability. - Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
350 lines
11 KiB
Python
350 lines
11 KiB
Python
"""
|
|
Serializers for Rides API following Django styleguide patterns.
|
|
"""
|
|
|
|
from rest_framework import serializers
|
|
from ..models import Ride, RideModel, Company
|
|
|
|
|
|
class RideModelOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for ride model data."""
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
description = serializers.CharField()
|
|
category = serializers.CharField()
|
|
manufacturer = serializers.SerializerMethodField()
|
|
|
|
def get_manufacturer(self, obj):
|
|
if obj.manufacturer:
|
|
return {
|
|
'id': obj.manufacturer.id,
|
|
'name': obj.manufacturer.name,
|
|
'slug': obj.manufacturer.slug
|
|
}
|
|
return None
|
|
|
|
|
|
class RideParkOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for ride's park data."""
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
|
|
|
|
class RideListOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for ride list view."""
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
category = serializers.CharField()
|
|
status = serializers.CharField()
|
|
description = serializers.CharField()
|
|
|
|
# Park info
|
|
park = RideParkOutputSerializer()
|
|
|
|
# Statistics
|
|
average_rating = serializers.DecimalField(max_digits=3, decimal_places=2, allow_null=True)
|
|
capacity_per_hour = serializers.IntegerField(allow_null=True)
|
|
|
|
# Dates
|
|
opening_date = serializers.DateField(allow_null=True)
|
|
closing_date = serializers.DateField(allow_null=True)
|
|
|
|
# Metadata
|
|
created_at = serializers.DateTimeField()
|
|
updated_at = serializers.DateTimeField()
|
|
|
|
|
|
class RideDetailOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for ride detail view."""
|
|
id = serializers.IntegerField()
|
|
name = serializers.CharField()
|
|
slug = serializers.CharField()
|
|
category = serializers.CharField()
|
|
status = serializers.CharField()
|
|
post_closing_status = serializers.CharField(allow_null=True)
|
|
description = serializers.CharField()
|
|
|
|
# Park info
|
|
park = RideParkOutputSerializer()
|
|
park_area = serializers.SerializerMethodField()
|
|
|
|
# Dates
|
|
opening_date = serializers.DateField(allow_null=True)
|
|
closing_date = serializers.DateField(allow_null=True)
|
|
status_since = serializers.DateField(allow_null=True)
|
|
|
|
# Physical specs
|
|
min_height_in = serializers.IntegerField(allow_null=True)
|
|
max_height_in = serializers.IntegerField(allow_null=True)
|
|
capacity_per_hour = serializers.IntegerField(allow_null=True)
|
|
ride_duration_seconds = serializers.IntegerField(allow_null=True)
|
|
|
|
# Statistics
|
|
average_rating = serializers.DecimalField(max_digits=3, decimal_places=2, allow_null=True)
|
|
|
|
# Companies
|
|
manufacturer = serializers.SerializerMethodField()
|
|
designer = serializers.SerializerMethodField()
|
|
|
|
# Model
|
|
ride_model = RideModelOutputSerializer(allow_null=True)
|
|
|
|
# Metadata
|
|
created_at = serializers.DateTimeField()
|
|
updated_at = serializers.DateTimeField()
|
|
|
|
def get_park_area(self, obj):
|
|
if obj.park_area:
|
|
return {
|
|
'id': obj.park_area.id,
|
|
'name': obj.park_area.name,
|
|
'slug': obj.park_area.slug
|
|
}
|
|
return None
|
|
|
|
def get_manufacturer(self, obj):
|
|
if obj.manufacturer:
|
|
return {
|
|
'id': obj.manufacturer.id,
|
|
'name': obj.manufacturer.name,
|
|
'slug': obj.manufacturer.slug
|
|
}
|
|
return None
|
|
|
|
def get_designer(self, obj):
|
|
if obj.designer:
|
|
return {
|
|
'id': obj.designer.id,
|
|
'name': obj.designer.name,
|
|
'slug': obj.designer.slug
|
|
}
|
|
return None
|
|
|
|
|
|
class RideCreateInputSerializer(serializers.Serializer):
|
|
"""Input serializer for creating rides."""
|
|
name = serializers.CharField(max_length=255)
|
|
description = serializers.CharField(allow_blank=True, default="")
|
|
category = serializers.ChoiceField(choices=Ride.CATEGORY_CHOICES)
|
|
status = serializers.ChoiceField(
|
|
choices=Ride.STATUS_CHOICES,
|
|
default="OPERATING"
|
|
)
|
|
|
|
# Required park
|
|
park_id = serializers.IntegerField()
|
|
|
|
# Optional area
|
|
park_area_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
# Optional dates
|
|
opening_date = serializers.DateField(required=False, allow_null=True)
|
|
closing_date = serializers.DateField(required=False, allow_null=True)
|
|
status_since = serializers.DateField(required=False, allow_null=True)
|
|
|
|
# Optional specs
|
|
min_height_in = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=30,
|
|
max_value=90
|
|
)
|
|
max_height_in = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=30,
|
|
max_value=90
|
|
)
|
|
capacity_per_hour = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=1
|
|
)
|
|
ride_duration_seconds = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=1
|
|
)
|
|
|
|
# Optional companies
|
|
manufacturer_id = serializers.IntegerField(required=False, allow_null=True)
|
|
designer_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
# Optional model
|
|
ride_model_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
def validate(self, data):
|
|
"""Cross-field validation."""
|
|
# Date 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"
|
|
)
|
|
|
|
# Height validation
|
|
min_height = data.get('min_height_in')
|
|
max_height = data.get('max_height_in')
|
|
|
|
if min_height and max_height and min_height > max_height:
|
|
raise serializers.ValidationError(
|
|
"Minimum height cannot be greater than maximum height"
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class RideUpdateInputSerializer(serializers.Serializer):
|
|
"""Input serializer for updating rides."""
|
|
name = serializers.CharField(max_length=255, required=False)
|
|
description = serializers.CharField(allow_blank=True, required=False)
|
|
category = serializers.ChoiceField(choices=Ride.CATEGORY_CHOICES, required=False)
|
|
status = serializers.ChoiceField(choices=Ride.STATUS_CHOICES, required=False)
|
|
post_closing_status = serializers.ChoiceField(
|
|
choices=Ride.POST_CLOSING_STATUS_CHOICES,
|
|
required=False,
|
|
allow_null=True
|
|
)
|
|
|
|
# Park and area
|
|
park_id = serializers.IntegerField(required=False)
|
|
park_area_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
# Dates
|
|
opening_date = serializers.DateField(required=False, allow_null=True)
|
|
closing_date = serializers.DateField(required=False, allow_null=True)
|
|
status_since = serializers.DateField(required=False, allow_null=True)
|
|
|
|
# Specs
|
|
min_height_in = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=30,
|
|
max_value=90
|
|
)
|
|
max_height_in = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=30,
|
|
max_value=90
|
|
)
|
|
capacity_per_hour = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=1
|
|
)
|
|
ride_duration_seconds = serializers.IntegerField(
|
|
required=False,
|
|
allow_null=True,
|
|
min_value=1
|
|
)
|
|
|
|
# Companies
|
|
manufacturer_id = serializers.IntegerField(required=False, allow_null=True)
|
|
designer_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
# Model
|
|
ride_model_id = serializers.IntegerField(required=False, allow_null=True)
|
|
|
|
def validate(self, data):
|
|
"""Cross-field validation."""
|
|
# Date 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"
|
|
)
|
|
|
|
# Height validation
|
|
min_height = data.get('min_height_in')
|
|
max_height = data.get('max_height_in')
|
|
|
|
if min_height and max_height and min_height > max_height:
|
|
raise serializers.ValidationError(
|
|
"Minimum height cannot be greater than maximum height"
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class RideFilterInputSerializer(serializers.Serializer):
|
|
"""Input serializer for ride filtering and search."""
|
|
# Search
|
|
search = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
# Category filter
|
|
category = serializers.MultipleChoiceField(
|
|
choices=Ride.CATEGORY_CHOICES,
|
|
required=False
|
|
)
|
|
|
|
# Status filter
|
|
status = serializers.MultipleChoiceField(
|
|
choices=Ride.STATUS_CHOICES,
|
|
required=False
|
|
)
|
|
|
|
# Park filter
|
|
park_id = serializers.IntegerField(required=False)
|
|
park_slug = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
# Company filters
|
|
manufacturer_id = serializers.IntegerField(required=False)
|
|
designer_id = serializers.IntegerField(required=False)
|
|
|
|
# Rating filter
|
|
min_rating = serializers.DecimalField(
|
|
max_digits=3,
|
|
decimal_places=2,
|
|
required=False,
|
|
min_value=1,
|
|
max_value=10
|
|
)
|
|
|
|
# Height filters
|
|
min_height_requirement = serializers.IntegerField(required=False)
|
|
max_height_requirement = serializers.IntegerField(required=False)
|
|
|
|
# Capacity filter
|
|
min_capacity = serializers.IntegerField(required=False)
|
|
|
|
# Ordering
|
|
ordering = serializers.ChoiceField(
|
|
choices=[
|
|
'name', '-name',
|
|
'opening_date', '-opening_date',
|
|
'average_rating', '-average_rating',
|
|
'capacity_per_hour', '-capacity_per_hour',
|
|
'created_at', '-created_at'
|
|
],
|
|
required=False,
|
|
default='name'
|
|
)
|
|
|
|
|
|
class RideStatsOutputSerializer(serializers.Serializer):
|
|
"""Output serializer for ride statistics."""
|
|
total_rides = serializers.IntegerField()
|
|
operating_rides = serializers.IntegerField()
|
|
closed_rides = serializers.IntegerField()
|
|
under_construction = serializers.IntegerField()
|
|
|
|
# By category
|
|
rides_by_category = serializers.DictField()
|
|
|
|
# Averages
|
|
average_rating = serializers.DecimalField(max_digits=3, decimal_places=2, allow_null=True)
|
|
average_capacity = serializers.DecimalField(max_digits=8, decimal_places=2, allow_null=True)
|
|
|
|
# Top manufacturers
|
|
top_manufacturers = serializers.ListField(child=serializers.DictField())
|
|
|
|
# Recently added
|
|
recently_added_count = serializers.IntegerField()
|