mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:51:08 -05:00
Add comprehensive tests for Parks API and models
- 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.
This commit is contained in:
281
parks/managers.py
Normal file
281
parks/managers.py
Normal file
@@ -0,0 +1,281 @@
|
||||
"""
|
||||
Custom managers and QuerySets for Parks models.
|
||||
Optimized queries following Django styleguide patterns.
|
||||
"""
|
||||
|
||||
from typing import Optional, List, Dict, Any, Union
|
||||
from django.db import models
|
||||
from django.db.models import Q, F, Count, Avg, Max, Min, Prefetch
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.gis.measure import Distance
|
||||
|
||||
from core.managers import (
|
||||
BaseQuerySet, BaseManager, LocationQuerySet, LocationManager,
|
||||
ReviewableQuerySet, ReviewableManager, StatusQuerySet, StatusManager
|
||||
)
|
||||
|
||||
|
||||
class ParkQuerySet(StatusQuerySet, ReviewableQuerySet, LocationQuerySet):
|
||||
"""Optimized QuerySet for Park model."""
|
||||
|
||||
def with_complete_stats(self):
|
||||
"""Add comprehensive park statistics."""
|
||||
return self.annotate(
|
||||
ride_count_calculated=Count('rides', distinct=True),
|
||||
coaster_count_calculated=Count(
|
||||
'rides',
|
||||
filter=Q(rides__category__in=['RC', 'WC']),
|
||||
distinct=True
|
||||
),
|
||||
area_count=Count('areas', distinct=True),
|
||||
review_count=Count('reviews', filter=Q(reviews__is_published=True), distinct=True),
|
||||
average_rating_calculated=Avg('reviews__rating', filter=Q(reviews__is_published=True)),
|
||||
latest_ride_opening=Max('rides__opening_date'),
|
||||
oldest_ride_opening=Min('rides__opening_date')
|
||||
)
|
||||
|
||||
def optimized_for_list(self):
|
||||
"""Optimize for park list display."""
|
||||
return self.select_related(
|
||||
'operator',
|
||||
'property_owner'
|
||||
).prefetch_related(
|
||||
'location'
|
||||
).with_complete_stats()
|
||||
|
||||
def optimized_for_detail(self):
|
||||
"""Optimize for park detail display."""
|
||||
from rides.models import Ride
|
||||
from .models import ParkReview
|
||||
|
||||
return self.select_related(
|
||||
'operator',
|
||||
'property_owner'
|
||||
).prefetch_related(
|
||||
'location',
|
||||
'areas',
|
||||
Prefetch(
|
||||
'rides',
|
||||
queryset=Ride.objects.select_related(
|
||||
'manufacturer', 'designer', 'ride_model', 'park_area'
|
||||
).order_by('name')
|
||||
),
|
||||
Prefetch(
|
||||
'reviews',
|
||||
queryset=ParkReview.objects.select_related('user')
|
||||
.filter(is_published=True)
|
||||
.order_by('-created_at')[:10]
|
||||
),
|
||||
'photos'
|
||||
)
|
||||
|
||||
def by_operator(self, *, operator_id: int):
|
||||
"""Filter parks by operator."""
|
||||
return self.filter(operator_id=operator_id)
|
||||
|
||||
def by_property_owner(self, *, owner_id: int):
|
||||
"""Filter parks by property owner."""
|
||||
return self.filter(property_owner_id=owner_id)
|
||||
|
||||
def with_minimum_coasters(self, *, min_coasters: int = 5):
|
||||
"""Filter parks with minimum number of coasters."""
|
||||
return self.with_complete_stats().filter(coaster_count_calculated__gte=min_coasters)
|
||||
|
||||
def large_parks(self, *, min_acres: float = 100.0):
|
||||
"""Filter for large parks."""
|
||||
return self.filter(size_acres__gte=min_acres)
|
||||
|
||||
def seasonal_parks(self):
|
||||
"""Filter for parks with seasonal operation."""
|
||||
return self.exclude(operating_season__exact='')
|
||||
|
||||
def for_map_display(self, *, bounds=None):
|
||||
"""Optimize for map display with minimal data."""
|
||||
queryset = self.select_related('operator').prefetch_related('location')
|
||||
|
||||
if bounds:
|
||||
queryset = queryset.within_bounds(
|
||||
north=bounds.north,
|
||||
south=bounds.south,
|
||||
east=bounds.east,
|
||||
west=bounds.west
|
||||
)
|
||||
|
||||
return queryset.values(
|
||||
'id', 'name', 'slug', 'status',
|
||||
'location__latitude', 'location__longitude',
|
||||
'location__city', 'location__state', 'location__country',
|
||||
'operator__name'
|
||||
)
|
||||
|
||||
def search_autocomplete(self, *, query: str, limit: int = 10):
|
||||
"""Optimized search for autocomplete."""
|
||||
return self.filter(
|
||||
Q(name__icontains=query) |
|
||||
Q(location__city__icontains=query) |
|
||||
Q(location__state__icontains=query)
|
||||
).select_related('operator', 'location').values(
|
||||
'id', 'name', 'slug',
|
||||
'location__city', 'location__state',
|
||||
'operator__name'
|
||||
)[:limit]
|
||||
|
||||
|
||||
class ParkManager(StatusManager, ReviewableManager, LocationManager):
|
||||
"""Custom manager for Park model."""
|
||||
|
||||
def get_queryset(self):
|
||||
return ParkQuerySet(self.model, using=self._db)
|
||||
|
||||
def with_complete_stats(self):
|
||||
return self.get_queryset().with_complete_stats()
|
||||
|
||||
def optimized_for_list(self):
|
||||
return self.get_queryset().optimized_for_list()
|
||||
|
||||
def optimized_for_detail(self):
|
||||
return self.get_queryset().optimized_for_detail()
|
||||
|
||||
def by_operator(self, *, operator_id: int):
|
||||
return self.get_queryset().by_operator(operator_id=operator_id)
|
||||
|
||||
def large_parks(self, *, min_acres: float = 100.0):
|
||||
return self.get_queryset().large_parks(min_acres=min_acres)
|
||||
|
||||
def for_map_display(self, *, bounds=None):
|
||||
return self.get_queryset().for_map_display(bounds=bounds)
|
||||
|
||||
|
||||
class ParkAreaQuerySet(BaseQuerySet):
|
||||
"""QuerySet for ParkArea model."""
|
||||
|
||||
def with_ride_counts(self):
|
||||
"""Add ride count annotations."""
|
||||
return self.annotate(
|
||||
ride_count=Count('rides', distinct=True),
|
||||
coaster_count=Count(
|
||||
'rides',
|
||||
filter=Q(rides__category__in=['RC', 'WC']),
|
||||
distinct=True
|
||||
)
|
||||
)
|
||||
|
||||
def optimized_for_list(self):
|
||||
"""Optimize for area list display."""
|
||||
return self.select_related('park').with_ride_counts()
|
||||
|
||||
def by_park(self, *, park_id: int):
|
||||
"""Filter areas by park."""
|
||||
return self.filter(park_id=park_id)
|
||||
|
||||
def with_rides(self):
|
||||
"""Filter areas that have rides."""
|
||||
return self.filter(rides__isnull=False).distinct()
|
||||
|
||||
|
||||
class ParkAreaManager(BaseManager):
|
||||
"""Manager for ParkArea model."""
|
||||
|
||||
def get_queryset(self):
|
||||
return ParkAreaQuerySet(self.model, using=self._db)
|
||||
|
||||
def with_ride_counts(self):
|
||||
return self.get_queryset().with_ride_counts()
|
||||
|
||||
def by_park(self, *, park_id: int):
|
||||
return self.get_queryset().by_park(park_id=park_id)
|
||||
|
||||
|
||||
class ParkReviewQuerySet(ReviewableQuerySet):
|
||||
"""QuerySet for ParkReview model."""
|
||||
|
||||
def for_park(self, *, park_id: int):
|
||||
"""Filter reviews for a specific park."""
|
||||
return self.filter(park_id=park_id)
|
||||
|
||||
def by_user(self, *, user_id: int):
|
||||
"""Filter reviews by user."""
|
||||
return self.filter(user_id=user_id)
|
||||
|
||||
def by_rating_range(self, *, min_rating: int = 1, max_rating: int = 10):
|
||||
"""Filter reviews by rating range."""
|
||||
return self.filter(rating__gte=min_rating, rating__lte=max_rating)
|
||||
|
||||
def optimized_for_display(self):
|
||||
"""Optimize for review display."""
|
||||
return self.select_related('user', 'park', 'moderated_by')
|
||||
|
||||
def recent_reviews(self, *, days: int = 30):
|
||||
"""Get recent reviews."""
|
||||
return self.recent(days=days)
|
||||
|
||||
def moderation_required(self):
|
||||
"""Filter reviews requiring moderation."""
|
||||
return self.filter(
|
||||
Q(is_published=False) |
|
||||
Q(moderated_at__isnull=True)
|
||||
)
|
||||
|
||||
|
||||
class ParkReviewManager(BaseManager):
|
||||
"""Manager for ParkReview model."""
|
||||
|
||||
def get_queryset(self):
|
||||
return ParkReviewQuerySet(self.model, using=self._db)
|
||||
|
||||
def for_park(self, *, park_id: int):
|
||||
return self.get_queryset().for_park(park_id=park_id)
|
||||
|
||||
def by_rating_range(self, *, min_rating: int = 1, max_rating: int = 10):
|
||||
return self.get_queryset().by_rating_range(min_rating=min_rating, max_rating=max_rating)
|
||||
|
||||
def moderation_required(self):
|
||||
return self.get_queryset().moderation_required()
|
||||
|
||||
|
||||
class CompanyQuerySet(BaseQuerySet):
|
||||
"""QuerySet for Company model."""
|
||||
|
||||
def operators(self):
|
||||
"""Filter for companies that operate parks."""
|
||||
return self.filter(roles__contains=['OPERATOR'])
|
||||
|
||||
def property_owners(self):
|
||||
"""Filter for companies that own park properties."""
|
||||
return self.filter(roles__contains=['PROPERTY_OWNER'])
|
||||
|
||||
def manufacturers(self):
|
||||
"""Filter for ride manufacturers."""
|
||||
return self.filter(roles__contains=['MANUFACTURER'])
|
||||
|
||||
def with_park_counts(self):
|
||||
"""Add park count annotations."""
|
||||
return self.annotate(
|
||||
operated_parks_count=Count('operated_parks', distinct=True),
|
||||
owned_parks_count=Count('owned_parks', distinct=True),
|
||||
total_parks_involvement=Count('operated_parks', distinct=True) + Count('owned_parks', distinct=True)
|
||||
)
|
||||
|
||||
def major_operators(self, *, min_parks: int = 5):
|
||||
"""Filter for major park operators."""
|
||||
return self.operators().with_park_counts().filter(operated_parks_count__gte=min_parks)
|
||||
|
||||
def optimized_for_list(self):
|
||||
"""Optimize for company list display."""
|
||||
return self.with_park_counts()
|
||||
|
||||
|
||||
class CompanyManager(BaseManager):
|
||||
"""Manager for Company model."""
|
||||
|
||||
def get_queryset(self):
|
||||
return CompanyQuerySet(self.model, using=self._db)
|
||||
|
||||
def operators(self):
|
||||
return self.get_queryset().operators()
|
||||
|
||||
def manufacturers(self):
|
||||
return self.get_queryset().manufacturers()
|
||||
|
||||
def major_operators(self, *, min_parks: int = 5):
|
||||
return self.get_queryset().major_operators(min_parks=min_parks)
|
||||
Reference in New Issue
Block a user