mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 08:51:09 -05:00
397 lines
13 KiB
Markdown
397 lines
13 KiB
Markdown
# 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, RELOCATED
|
|
- `opening_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 ParkArea
|
|
- `rides` - Reverse relation from rides app
|
|
|
|
**Custom Methods**:
|
|
- `get_absolute_url()` - Returns park detail URL
|
|
- `get_status_color()` - Returns Tailwind CSS classes for status display
|
|
- `formatted_location` (property) - Returns formatted address string
|
|
- `coordinates` (property) - Returns (lat, lon) tuple
|
|
- `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup
|
|
- `save()` - 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 URL
|
|
- `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup
|
|
- `save()` - 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 URL
|
|
- `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup
|
|
- `save()` - 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 URL
|
|
- `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup
|
|
- `save()` - 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_at` and `updated_at` fields
|
|
- Includes `get_history()` method for pghistory integration
|
|
|
|
#### HistoricalSlug (`history_tracking/models.py`)
|
|
**Tracks historical slugs for all models**
|
|
- `content_type` - ForeignKey to ContentType
|
|
- `object_id` - PositiveIntegerField
|
|
- `slug` - SlugField(max_length=255)
|
|
- `created_at` - DateTimeField
|
|
- `user` - ForeignKey to User (optional)
|
|
|
|
## Relationship Diagram
|
|
|
|
```mermaid
|
|
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_fields` for slug generation
|
|
- Both include `created_at` and `updated_at` as 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 `HistoricalSlug` model
|
|
|
|
### History Tracking
|
|
Implemented via two mechanisms:
|
|
1. **pghistory**: Automatic tracking with `@pghistory.track()` decorator
|
|
2. **Manual tracking**: `HistoricalSlug` model for slug changes
|
|
3. **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 `HistoricalSlug` records
|
|
- 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
|
|
1. **Consistent patterns**: All models follow similar slug generation and history tracking patterns
|
|
2. **Comprehensive history**: Both automatic (pghistory) and manual (HistoricalSlug) tracking
|
|
3. **Good separation of concerns**: Clear distinction between operators and property owners
|
|
4. **Rich relationships**: Proper foreign key relationships with appropriate related_names
|
|
|
|
### Issues
|
|
1. **Artificial separation**: Three apps that are always used together
|
|
2. **Duplicated code**: Similar admin configurations and view patterns across apps
|
|
3. **Complex imports**: Cross-app imports create coupling
|
|
4. **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
|
|
1. **Reduced complexity**: Single app to manage instead of three
|
|
2. **Eliminated duplication**: Shared admin mixins, view base classes, and template components
|
|
3. **Simplified imports**: No cross-app dependencies
|
|
4. **Better cohesion**: Related functionality grouped together
|
|
5. **Easier maintenance**: Single location for park domain logic
|
|
|
|
### Migration Strategy
|
|
1. Create new model structure within parks app
|
|
2. Move existing models to new locations
|
|
3. Update all imports and references
|
|
4. Consolidate admin configurations
|
|
5. Merge URL patterns
|
|
6. Update template references
|
|
7. Run Django migrations to reflect changes
|
|
8. Remove empty apps
|
|
|
|
This consolidation maintains all existing functionality while significantly improving code organization and maintainability. |