- Implement tests for RideLocation and CompanyHeadquarters models to verify functionality and data integrity. - Create a manual trigger test script for trending content calculation endpoint, including authentication and unauthorized access tests. - Develop a manufacturer sync test to ensure ride manufacturers are correctly associated with ride models. - Add tests for ParkLocation model, including coordinate setting and distance calculations between parks. - Implement a RoadTripService test suite covering geocoding, route calculation, park discovery, and error handling. - Create a unified map service test script to validate map functionality, API endpoints, and performance metrics.
13 KiB
ThrillWiki Park Domain Analysis
Executive Summary
This document provides a complete inventory of all park-related models and their relationships across the ThrillWiki Django codebase. The analysis reveals that park-related functionality is currently distributed across three separate Django apps (parks, operators, and property_owners) that are always used together but artificially separated.
Current Architecture Overview
Apps Structure
- parks/ - Core park and park area models
- operators/ - Companies that operate theme parks
- property_owners/ - Companies that own park property
- companies/ - Empty models directory (no active models found)
Model Inventory
Parks App Models
Park Model (parks/models.py)
Location: parks.models.Park
Inheritance: TrackedModel (provides history tracking)
Decorators: @pghistory.track()
Fields:
name- CharField(max_length=255)slug- SlugField(max_length=255, unique=True)description- TextField(blank=True)status- CharField with choices: OPERATING, CLOSED_TEMP, CLOSED_PERM, UNDER_CONSTRUCTION, DEMOLISHED, RELOCATEDopening_date- DateField(null=True, blank=True)closing_date- DateField(null=True, blank=True)operating_season- CharField(max_length=255, blank=True)size_acres- DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)website- URLField(blank=True)average_rating- DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)ride_count- IntegerField(null=True, blank=True)coaster_count- IntegerField(null=True, blank=True)created_at- DateTimeField(auto_now_add=True, null=True)updated_at- DateTimeField(auto_now=True)
Relationships:
operator- ForeignKey to Operator (SET_NULL, null=True, blank=True, related_name="parks")property_owner- ForeignKey to PropertyOwner (SET_NULL, null=True, blank=True, related_name="owned_parks")location- GenericRelation to Location (related_query_name='park')photos- GenericRelation to Photo (related_query_name="park")areas- Reverse relation from ParkArearides- Reverse relation from rides app
Custom Methods:
get_absolute_url()- Returns park detail URLget_status_color()- Returns Tailwind CSS classes for status displayformatted_location(property) - Returns formatted address stringcoordinates(property) - Returns (lat, lon) tupleget_by_slug(slug)(classmethod) - Handles current and historical slug lookupsave()- Custom save with slug generation and historical slug tracking
Meta Options:
ordering = ["name"]
ParkArea Model (parks/models.py)
Location: parks.models.ParkArea
Inheritance: TrackedModel
Decorators: @pghistory.track()
Fields:
name- CharField(max_length=255)slug- SlugField(max_length=255)description- TextField(blank=True)opening_date- DateField(null=True, blank=True)closing_date- DateField(null=True, blank=True)created_at- DateTimeField(auto_now_add=True, null=True)updated_at- DateTimeField(auto_now=True)
Relationships:
park- ForeignKey to Park (CASCADE, related_name="areas")
Custom Methods:
get_absolute_url()- Returns area detail URLget_by_slug(slug)(classmethod) - Handles current and historical slug lookupsave()- Auto-generates slug from name
Meta Options:
ordering = ["name"]unique_together = ["park", "slug"]
Operators App Models
Operator Model (operators/models.py)
Location: operators.models.Operator
Inheritance: TrackedModel
Decorators: @pghistory.track()
Fields:
name- CharField(max_length=255)slug- SlugField(max_length=255, unique=True)description- TextField(blank=True)website- URLField(blank=True)founded_year- PositiveIntegerField(blank=True, null=True)headquarters- CharField(max_length=255, blank=True)parks_count- IntegerField(default=0)rides_count- IntegerField(default=0)
Custom Methods:
get_absolute_url()- Returns operator detail URLget_by_slug(slug)(classmethod) - Handles current and historical slug lookupsave()- Auto-generates slug if missing
Meta Options:
ordering = ['name']verbose_name = 'Operator'verbose_name_plural = 'Operators'
Property Owners App Models
PropertyOwner Model (property_owners/models.py)
Location: property_owners.models.PropertyOwner
Inheritance: TrackedModel
Decorators: @pghistory.track()
Fields:
name- CharField(max_length=255)slug- SlugField(max_length=255, unique=True)description- TextField(blank=True)website- URLField(blank=True)
Custom Methods:
get_absolute_url()- Returns property owner detail URLget_by_slug(slug)(classmethod) - Handles current and historical slug lookupsave()- Auto-generates slug if missing
Meta Options:
ordering = ['name']verbose_name = 'Property Owner'verbose_name_plural = 'Property Owners'
Related Models from Other Apps
TrackedModel (history_tracking/models.py)
Base class for all park-related models
- Provides
created_atandupdated_atfields - Includes
get_history()method for pghistory integration
HistoricalSlug (history_tracking/models.py)
Tracks historical slugs for all models
content_type- ForeignKey to ContentTypeobject_id- PositiveIntegerFieldslug- SlugField(max_length=255)created_at- DateTimeFielduser- ForeignKey to User (optional)
Relationship Diagram
erDiagram
Park ||--o{ ParkArea : contains
Park }o--|| Operator : operated_by
Park }o--o| PropertyOwner : owned_by
Park ||--o{ Location : has_location
Park ||--o{ Photo : has_photos
Park ||--o{ Ride : contains
Operator {
int id PK
string name
string slug UK
text description
string website
int founded_year
string headquarters
int parks_count
int rides_count
datetime created_at
datetime updated_at
}
PropertyOwner {
int id PK
string name
string slug UK
text description
string website
datetime created_at
datetime updated_at
}
Park {
int id PK
string name
string slug UK
text description
string status
date opening_date
date closing_date
string operating_season
decimal size_acres
string website
decimal average_rating
int ride_count
int coaster_count
int operator_id FK
int property_owner_id FK
datetime created_at
datetime updated_at
}
ParkArea {
int id PK
string name
string slug
text description
date opening_date
date closing_date
int park_id FK
datetime created_at
datetime updated_at
}
Location {
int id PK
int content_type_id FK
int object_id
string name
string location_type
decimal latitude
decimal longitude
string street_address
string city
string state
string country
string postal_code
}
Photo {
int id PK
int content_type_id FK
int object_id
string image
string caption
int uploaded_by_id FK
}
Ride {
int id PK
string name
string slug
text description
string category
string status
int park_id FK
int park_area_id FK
int manufacturer_id FK
int designer_id FK
}
Admin Configurations
Parks App Admin
- ParkAdmin: List display includes location, status, operator, property_owner
- ParkAreaAdmin: List display includes park relationship
- Both use
prepopulated_fieldsfor slug generation - Both include
created_atandupdated_atas readonly fields
Operators App Admin
- OperatorAdmin: Shows parks_count and rides_count (readonly)
- Includes founded_year filter
- Search includes name, description, headquarters
Property Owners App Admin
- PropertyOwnerAdmin: Basic configuration
- Search includes name and description
- No special filters or readonly fields
URL Patterns and Views
Parks App URLs (parks/urls.py)
- List/Search:
/- ParkSearchView with autocomplete - Create:
/create/- ParkCreateView - Detail:
/<slug>/- ParkDetailView with slug redirect support - Update:
/<slug>/edit/- ParkUpdateView - Areas:
/<park_slug>/areas/<area_slug>/- ParkAreaDetailView - HTMX Endpoints: Various endpoints for dynamic content loading
- Park-specific ride categories: Multiple URL patterns for different ride types
Operators App URLs (operators/urls.py)
- List:
/- OperatorListView - Detail:
/<slug>/- OperatorDetailView with slug redirect support
Property Owners App URLs (property_owners/urls.py)
- List:
/- PropertyOwnerListView - Detail:
/<slug>/- PropertyOwnerDetailView with slug redirect support
Template Usage Patterns
Template Structure
- parks/park_detail.html: Comprehensive park display with operator/property owner links
- operators/operator_detail.html: Shows operated parks with park links
- property_owners/property_owner_detail.html: Shows owned properties with operator info
Key Template Features
- Cross-linking between parks, operators, and property owners
- Conditional display of property owner (only if different from operator)
- Status badges with Tailwind CSS classes
- Photo galleries and location maps
- History tracking display
Shared Functionality Patterns
Slug Generation
All models use consistent slug generation:
- Auto-generated from name field in
save()method - Uses Django's
slugify()function - Historical slug tracking via
HistoricalSlugmodel
History Tracking
Implemented via two mechanisms:
- pghistory: Automatic tracking with
@pghistory.track()decorator - Manual tracking:
HistoricalSlugmodel for slug changes - DiffMixin: Provides
diff_against_previous()method for change comparison
Slug Redirect Support
All detail views use SlugRedirectMixin and implement get_by_slug() classmethod:
- Checks current slug first
- Falls back to pghistory events
- Falls back to
HistoricalSlugrecords - Returns tuple of (object, was_redirect_needed)
Base Classes
- TrackedModel: Provides
created_at,updated_at, and history integration - SlugRedirectMixin: Handles historical slug redirects in views
Key Findings
Strengths
- Consistent patterns: All models follow similar slug generation and history tracking patterns
- Comprehensive history: Both automatic (pghistory) and manual (HistoricalSlug) tracking
- Good separation of concerns: Clear distinction between operators and property owners
- Rich relationships: Proper foreign key relationships with appropriate related_names
Issues
- Artificial separation: Three apps that are always used together
- Duplicated code: Similar admin configurations and view patterns across apps
- Complex imports: Cross-app imports create coupling
- Template redundancy: Similar template patterns across apps
Entity Relationship Compliance
The current implementation follows the specified entity relationship rules:
- ✅ Parks have required Operator relationship
- ✅ Parks have optional PropertyOwner relationship
- ✅ No direct Company entity references
- ✅ Proper foreign key relationships with null/blank settings
Consolidation Recommendations
Based on the analysis, I recommend consolidating the three apps into a single parks app with the following structure:
parks/
├── models/
│ ├── __init__.py # Import all models here
│ ├── park.py # Park, ParkArea models
│ ├── operators.py # Operator model
│ └── owners.py # PropertyOwner model
├── admin/
│ ├── __init__.py # Register all admin classes
│ ├── park.py # Park and ParkArea admin
│ ├── operators.py # Operator admin
│ └── owners.py # PropertyOwner admin
├── views/
│ ├── __init__.py # Import all views
│ ├── parks.py # Park and ParkArea views
│ ├── operators.py # Operator views
│ └── owners.py # PropertyOwner views
├── templates/parks/
│ ├── parks/ # Park templates
│ ├── operators/ # Operator templates
│ └── owners/ # Property owner templates
└── urls.py # All URL patterns
Benefits of Consolidation
- Reduced complexity: Single app to manage instead of three
- Eliminated duplication: Shared admin mixins, view base classes, and template components
- Simplified imports: No cross-app dependencies
- Better cohesion: Related functionality grouped together
- Easier maintenance: Single location for park domain logic
Migration Strategy
- Create new model structure within parks app
- Move existing models to new locations
- Update all imports and references
- Consolidate admin configurations
- Merge URL patterns
- Update template references
- Run Django migrations to reflect changes
- Remove empty apps
This consolidation maintains all existing functionality while significantly improving code organization and maintainability.