mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 17:11:09 -05:00
327 lines
7.4 KiB
Markdown
327 lines
7.4 KiB
Markdown
# Data Documentation
|
|
|
|
## Database Schema
|
|
|
|
### Core Models
|
|
|
|
#### Parks
|
|
```sql
|
|
CREATE TABLE parks_park (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) UNIQUE NOT NULL,
|
|
description TEXT,
|
|
status VARCHAR(20) DEFAULT 'OPERATING',
|
|
opening_date DATE,
|
|
closing_date DATE,
|
|
operating_season VARCHAR(255),
|
|
size_acres DECIMAL(10,2),
|
|
website VARCHAR(200),
|
|
average_rating DECIMAL(3,2),
|
|
ride_count INTEGER,
|
|
coaster_count INTEGER,
|
|
owner_id INTEGER REFERENCES companies_company(id),
|
|
created_at TIMESTAMP,
|
|
updated_at TIMESTAMP
|
|
);
|
|
```
|
|
|
|
#### Rides
|
|
```sql
|
|
CREATE TABLE rides_ride (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
status VARCHAR(20),
|
|
park_id INTEGER REFERENCES parks_park(id),
|
|
area_id INTEGER REFERENCES parks_parkarea(id),
|
|
manufacturer_id INTEGER REFERENCES companies_company(id),
|
|
designer_id INTEGER REFERENCES designers_designer(id),
|
|
opening_date DATE,
|
|
closing_date DATE,
|
|
height_requirement INTEGER,
|
|
ride_type VARCHAR(50),
|
|
thrill_rating INTEGER,
|
|
created_at TIMESTAMP,
|
|
updated_at TIMESTAMP,
|
|
UNIQUE(park_id, slug)
|
|
);
|
|
```
|
|
|
|
#### Reviews
|
|
```sql
|
|
CREATE TABLE reviews_review (
|
|
id SERIAL PRIMARY KEY,
|
|
content TEXT NOT NULL,
|
|
rating DECIMAL(3,2),
|
|
status VARCHAR(20),
|
|
author_id INTEGER REFERENCES auth_user(id),
|
|
content_type_id INTEGER REFERENCES django_content_type(id),
|
|
object_id INTEGER,
|
|
created_at TIMESTAMP,
|
|
updated_at TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### Entity Relationships
|
|
|
|
```mermaid
|
|
erDiagram
|
|
Park ||--o{ ParkArea : "contains"
|
|
Park ||--o{ Ride : "has"
|
|
Park ||--o{ Photo : "has"
|
|
Park ||--o{ Review : "receives"
|
|
ParkArea ||--o{ Ride : "contains"
|
|
Ride ||--o{ Photo : "has"
|
|
Ride ||--o{ Review : "receives"
|
|
Company ||--o{ Park : "owns"
|
|
Company ||--o{ Ride : "manufactures"
|
|
Designer ||--o{ Ride : "designs"
|
|
User ||--o{ Review : "writes"
|
|
```
|
|
|
|
## Data Models
|
|
|
|
### Content Models
|
|
|
|
#### Park Model
|
|
- Core information about theme parks
|
|
- Location data through GenericRelation
|
|
- Media attachments
|
|
- Historical tracking
|
|
- Owner relationship
|
|
|
|
#### Ride Model
|
|
- Technical specifications
|
|
- Park and area relationships
|
|
- Manufacturer and designer links
|
|
- Operation status tracking
|
|
- Safety requirements
|
|
|
|
#### Review Model
|
|
- Generic foreign key for flexibility
|
|
- Rating system
|
|
- Media attachments
|
|
- Moderation status
|
|
- Author tracking
|
|
|
|
### Supporting Models
|
|
|
|
#### Location Model
|
|
```python
|
|
class Location(models.Model):
|
|
content_type = models.ForeignKey(ContentType)
|
|
object_id = models.PositiveIntegerField()
|
|
content_object = GenericForeignKey()
|
|
|
|
address = models.CharField(max_length=255)
|
|
city = models.CharField(max_length=100)
|
|
state = models.CharField(max_length=100)
|
|
country = models.CharField(max_length=100)
|
|
postal_code = models.CharField(max_length=20)
|
|
latitude = models.DecimalField(max_digits=9, decimal_places=6)
|
|
longitude = models.DecimalField(max_digits=9, decimal_places=6)
|
|
```
|
|
|
|
#### Media Model
|
|
```python
|
|
class Photo(models.Model):
|
|
content_type = models.ForeignKey(ContentType)
|
|
object_id = models.PositiveIntegerField()
|
|
content_object = GenericForeignKey()
|
|
|
|
file = models.ImageField(upload_to='photos/')
|
|
caption = models.CharField(max_length=255)
|
|
taken_at = models.DateTimeField(null=True)
|
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
```
|
|
|
|
## Storage Strategies
|
|
|
|
### Database Storage
|
|
|
|
#### PostgreSQL Configuration
|
|
```python
|
|
DATABASES = {
|
|
'default': {
|
|
'ENGINE': 'django.db.backends.postgresql',
|
|
'NAME': 'thrillwiki',
|
|
'CONN_MAX_AGE': 60,
|
|
'OPTIONS': {
|
|
'client_encoding': 'UTF8',
|
|
},
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Indexing Strategy
|
|
```sql
|
|
-- Performance indexes
|
|
CREATE INDEX idx_park_slug ON parks_park(slug);
|
|
CREATE INDEX idx_ride_slug ON rides_ride(slug);
|
|
CREATE INDEX idx_review_content_type ON reviews_review(content_type_id, object_id);
|
|
```
|
|
|
|
### File Storage
|
|
|
|
#### Media Storage
|
|
```python
|
|
# Media storage configuration
|
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
|
MEDIA_URL = '/media/'
|
|
|
|
# File upload handlers
|
|
FILE_UPLOAD_HANDLERS = [
|
|
'django.core.files.uploadhandler.MemoryFileUploadHandler',
|
|
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
|
|
]
|
|
```
|
|
|
|
#### Directory Structure
|
|
```
|
|
media/
|
|
├── photos/
|
|
│ ├── parks/
|
|
│ ├── rides/
|
|
│ └── reviews/
|
|
├── avatars/
|
|
└── documents/
|
|
```
|
|
|
|
### Caching Strategy
|
|
|
|
#### Cache Configuration
|
|
```python
|
|
CACHES = {
|
|
'default': {
|
|
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
|
'LOCATION': 'redis://127.0.0.1:6379/1',
|
|
'OPTIONS': {
|
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Cache Keys
|
|
```python
|
|
# Cache key patterns
|
|
CACHE_KEYS = {
|
|
'park_detail': 'park:{slug}',
|
|
'ride_list': 'park:{park_slug}:rides',
|
|
'review_count': 'content:{type}:{id}:reviews',
|
|
}
|
|
```
|
|
|
|
## Data Migration
|
|
|
|
### Migration Strategy
|
|
1. Schema migrations via Django
|
|
2. Data migrations for model changes
|
|
3. Content migrations for large updates
|
|
|
|
### Example Migration
|
|
```python
|
|
# migrations/0002_add_park_status.py
|
|
from django.db import migrations, models
|
|
|
|
class Migration(migrations.Migration):
|
|
dependencies = [
|
|
('parks', '0001_initial'),
|
|
]
|
|
|
|
operations = [
|
|
migrations.AddField(
|
|
model_name='park',
|
|
name='status',
|
|
field=models.CharField(
|
|
max_length=20,
|
|
choices=[
|
|
('OPERATING', 'Operating'),
|
|
('CLOSED', 'Closed'),
|
|
],
|
|
default='OPERATING'
|
|
),
|
|
),
|
|
]
|
|
```
|
|
|
|
## Data Protection
|
|
|
|
### Backup Strategy
|
|
1. Daily database backups
|
|
2. Media files backup
|
|
3. Retention policy management
|
|
|
|
### Backup Configuration
|
|
```python
|
|
# backup settings
|
|
BACKUP_ROOT = os.path.join(BASE_DIR, 'backups')
|
|
BACKUP_RETENTION_DAYS = 30
|
|
BACKUP_COMPRESSION = True
|
|
```
|
|
|
|
## Data Validation
|
|
|
|
### Model Validation
|
|
```python
|
|
class Park(models.Model):
|
|
def clean(self):
|
|
if self.closing_date and self.opening_date:
|
|
if self.closing_date < self.opening_date:
|
|
raise ValidationError({
|
|
'closing_date': 'Closing date cannot be before opening date'
|
|
})
|
|
```
|
|
|
|
### Form Validation
|
|
```python
|
|
class RideForm(forms.ModelForm):
|
|
def clean_height_requirement(self):
|
|
height = self.cleaned_data['height_requirement']
|
|
if height and height < 0:
|
|
raise forms.ValidationError('Height requirement cannot be negative')
|
|
return height
|
|
```
|
|
|
|
## Data Access Patterns
|
|
|
|
### QuerySet Optimization
|
|
```python
|
|
# Optimized query pattern
|
|
Park.objects.select_related('owner')\
|
|
.prefetch_related('rides', 'areas')\
|
|
.filter(status='OPERATING')
|
|
```
|
|
|
|
### Caching Pattern
|
|
```python
|
|
def get_park_detail(slug):
|
|
cache_key = f'park:{slug}'
|
|
park = cache.get(cache_key)
|
|
if not park:
|
|
park = Park.objects.get(slug=slug)
|
|
cache.set(cache_key, park, timeout=3600)
|
|
return park
|
|
```
|
|
|
|
## Monitoring and Metrics
|
|
|
|
### Database Metrics
|
|
- Query performance
|
|
- Cache hit rates
|
|
- Storage usage
|
|
- Connection pool status
|
|
|
|
### Collection Configuration
|
|
```python
|
|
LOGGING = {
|
|
'handlers': {
|
|
'db_log': {
|
|
'level': 'DEBUG',
|
|
'class': 'logging.handlers.RotatingFileHandler',
|
|
'filename': 'logs/db.log',
|
|
},
|
|
},
|
|
} |