feat: Implement initial schema and add various API, service, and management command enhancements across the application.

This commit is contained in:
pacnpal
2026-01-01 15:13:01 -05:00
parent c95f99ca10
commit b243b17af7
413 changed files with 11164 additions and 17433 deletions

View File

@@ -190,9 +190,7 @@ class Migration(migrations.Migration):
),
(
"average_rating",
models.DecimalField(
blank=True, decimal_places=2, max_digits=3, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True),
),
],
options={
@@ -374,21 +372,15 @@ class Migration(migrations.Migration):
),
(
"height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"length_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=7, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
),
(
"speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
("inversions", models.PositiveIntegerField(default=0)),
(
@@ -432,9 +424,7 @@ class Migration(migrations.Migration):
),
(
"max_drop_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"launch_type",
@@ -692,9 +682,7 @@ class Migration(migrations.Migration):
),
migrations.AddIndex(
model_name="ridelocation",
index=models.Index(
fields=["park_area"], name="rides_ridel_park_ar_26c90c_idx"
),
index=models.Index(fields=["park_area"], name="rides_ridel_park_ar_26c90c_idx"),
),
migrations.AlterUniqueTogether(
name="ridemodel",

View File

@@ -89,9 +89,7 @@ class Migration(migrations.Migration):
),
(
"average_rating",
models.DecimalField(
blank=True, decimal_places=2, max_digits=3, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True),
),
],
options={
@@ -140,21 +138,15 @@ class Migration(migrations.Migration):
("id", models.BigIntegerField()),
(
"height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"length_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=7, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
),
(
"speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
("inversions", models.PositiveIntegerField(default=0)),
(
@@ -198,9 +190,7 @@ class Migration(migrations.Migration):
),
(
"max_drop_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"launch_type",

View File

@@ -220,9 +220,7 @@ class Migration(migrations.Migration):
),
(
"rank",
models.PositiveIntegerField(
db_index=True, help_text="Overall rank position (1 = best)"
),
models.PositiveIntegerField(db_index=True, help_text="Overall rank position (1 = best)"),
),
(
"wins",
@@ -323,9 +321,7 @@ class Migration(migrations.Migration):
("id", models.BigIntegerField()),
(
"rank",
models.PositiveIntegerField(
help_text="Overall rank position (1 = best)"
),
models.PositiveIntegerField(help_text="Overall rank position (1 = best)"),
),
(
"wins",
@@ -487,15 +483,11 @@ class Migration(migrations.Migration):
),
migrations.AddIndex(
model_name="ridepaircomparison",
index=models.Index(
fields=["ride_a", "ride_b"], name="rides_ridep_ride_a__eb0674_idx"
),
index=models.Index(fields=["ride_a", "ride_b"], name="rides_ridep_ride_a__eb0674_idx"),
),
migrations.AddIndex(
model_name="ridepaircomparison",
index=models.Index(
fields=["last_calculated"], name="rides_ridep_last_ca_bd9f6c_idx"
),
index=models.Index(fields=["last_calculated"], name="rides_ridep_last_ca_bd9f6c_idx"),
),
migrations.AlterUniqueTogether(
name="ridepaircomparison",
@@ -551,9 +543,7 @@ class Migration(migrations.Migration):
migrations.AddConstraint(
model_name="rideranking",
constraint=models.CheckConstraint(
condition=models.Q(
("winning_percentage__gte", 0), ("winning_percentage__lte", 1)
),
condition=models.Q(("winning_percentage__gte", 0), ("winning_percentage__lte", 1)),
name="rideranking_winning_percentage_range",
violation_error_message="Winning percentage must be between 0 and 1",
),

View File

@@ -163,27 +163,19 @@ class Migration(migrations.Migration):
),
migrations.AddIndex(
model_name="ridephoto",
index=models.Index(
fields=["ride", "is_primary"], name="rides_ridep_ride_id_aa49f1_idx"
),
index=models.Index(fields=["ride", "is_primary"], name="rides_ridep_ride_id_aa49f1_idx"),
),
migrations.AddIndex(
model_name="ridephoto",
index=models.Index(
fields=["ride", "is_approved"], name="rides_ridep_ride_id_f1eddc_idx"
),
index=models.Index(fields=["ride", "is_approved"], name="rides_ridep_ride_id_f1eddc_idx"),
),
migrations.AddIndex(
model_name="ridephoto",
index=models.Index(
fields=["ride", "photo_type"], name="rides_ridep_ride_id_49e7ec_idx"
),
index=models.Index(fields=["ride", "photo_type"], name="rides_ridep_ride_id_49e7ec_idx"),
),
migrations.AddIndex(
model_name="ridephoto",
index=models.Index(
fields=["created_at"], name="rides_ridep_created_106e02_idx"
),
index=models.Index(fields=["created_at"], name="rides_ridep_created_106e02_idx"),
),
migrations.AddConstraint(
model_name="ridephoto",

View File

@@ -147,21 +147,15 @@ class Migration(migrations.Migration):
),
(
"spec_name",
models.CharField(
help_text="Name of the specification", max_length=100
),
models.CharField(help_text="Name of the specification", max_length=100),
),
(
"spec_value",
models.CharField(
help_text="Value of the specification", max_length=255
),
models.CharField(help_text="Value of the specification", max_length=255),
),
(
"spec_unit",
models.CharField(
blank=True, help_text="Unit of measurement", max_length=20
),
models.CharField(blank=True, help_text="Unit of measurement", max_length=20),
),
(
"notes",
@@ -203,21 +197,15 @@ class Migration(migrations.Migration):
),
(
"spec_name",
models.CharField(
help_text="Name of the specification", max_length=100
),
models.CharField(help_text="Name of the specification", max_length=100),
),
(
"spec_value",
models.CharField(
help_text="Value of the specification", max_length=255
),
models.CharField(help_text="Value of the specification", max_length=255),
),
(
"spec_unit",
models.CharField(
blank=True, help_text="Unit of measurement", max_length=20
),
models.CharField(blank=True, help_text="Unit of measurement", max_length=20),
),
(
"notes",
@@ -251,33 +239,23 @@ class Migration(migrations.Migration):
),
(
"description",
models.TextField(
blank=True, help_text="Description of variant differences"
),
models.TextField(blank=True, help_text="Description of variant differences"),
),
(
"min_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"max_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"min_speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
(
"max_speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
(
"distinguishing_features",
@@ -307,33 +285,23 @@ class Migration(migrations.Migration):
),
(
"description",
models.TextField(
blank=True, help_text="Description of variant differences"
),
models.TextField(blank=True, help_text="Description of variant differences"),
),
(
"min_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"max_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True),
),
(
"min_speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
(
"max_speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True),
),
(
"distinguishing_features",
@@ -750,9 +718,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodel",
name="description",
field=models.TextField(
blank=True, help_text="Detailed description of the ride model"
),
field=models.TextField(blank=True, help_text="Detailed description of the ride model"),
),
migrations.AlterField(
model_name="ridemodel",
@@ -794,9 +760,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelevent",
name="description",
field=models.TextField(
blank=True, help_text="Detailed description of the ride model"
),
field=models.TextField(blank=True, help_text="Detailed description of the ride model"),
),
migrations.AlterField(
model_name="ridemodelevent",

View File

@@ -13,8 +13,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodel",
name="slug",
field=models.SlugField(
help_text="URL-friendly identifier", max_length=255, unique=True
),
field=models.SlugField(help_text="URL-friendly identifier", max_length=255, unique=True),
),
]

View File

@@ -16,9 +16,7 @@ def update_ride_model_slugs(apps, schema_editor):
counter = 1
base_slug = new_slug
while (
RideModel.objects.filter(
manufacturer=ride_model.manufacturer, slug=new_slug
)
RideModel.objects.filter(manufacturer=ride_model.manufacturer, slug=new_slug)
.exclude(pk=ride_model.pk)
.exists()
):
@@ -37,16 +35,12 @@ def reverse_ride_model_slugs(apps, schema_editor):
for ride_model in RideModel.objects.all():
# Generate old-style slug with manufacturer + name
old_slug = slugify(
f"{ride_model.manufacturer.name if ride_model.manufacturer else ''} {ride_model.name}"
)
old_slug = slugify(f"{ride_model.manufacturer.name if ride_model.manufacturer else ''} {ride_model.name}")
# Ensure uniqueness globally (old way)
counter = 1
base_slug = old_slug
while (
RideModel.objects.filter(slug=old_slug).exclude(pk=ride_model.pk).exists()
):
while RideModel.objects.filter(slug=old_slug).exclude(pk=ride_model.pk).exists():
old_slug = f"{base_slug}-{counter}"
counter += 1

View File

@@ -39,16 +39,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name="company",
name="url",
field=models.URLField(
blank=True, help_text="Frontend URL for this company"
),
field=models.URLField(blank=True, help_text="Frontend URL for this company"),
),
migrations.AddField(
model_name="companyevent",
name="url",
field=models.URLField(
blank=True, help_text="Frontend URL for this company"
),
field=models.URLField(blank=True, help_text="Frontend URL for this company"),
),
migrations.AddField(
model_name="ride",
@@ -63,16 +59,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name="ridemodel",
name="url",
field=models.URLField(
blank=True, help_text="Frontend URL for this ride model"
),
field=models.URLField(blank=True, help_text="Frontend URL for this ride model"),
),
migrations.AddField(
model_name="ridemodelevent",
name="url",
field=models.URLField(
blank=True, help_text="Frontend URL for this ride model"
),
field=models.URLField(blank=True, help_text="Frontend URL for this ride model"),
),
pgtrigger.migrations.AddTrigger(
model_name="company",

View File

@@ -23,16 +23,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name="ride",
name="park_url",
field=models.URLField(
blank=True, help_text="Frontend URL for this ride's park"
),
field=models.URLField(blank=True, help_text="Frontend URL for this ride's park"),
),
migrations.AddField(
model_name="rideevent",
name="park_url",
field=models.URLField(
blank=True, help_text="Frontend URL for this ride's park"
),
field=models.URLField(blank=True, help_text="Frontend URL for this ride's park"),
),
pgtrigger.migrations.AddTrigger(
model_name="ride",

View File

@@ -12,13 +12,15 @@ from django.db import migrations
def populate_computed_fields(apps, schema_editor):
"""Populate computed fields for all existing rides."""
Ride = apps.get_model('rides', 'Ride')
Ride = apps.get_model("rides", "Ride")
# Disable pghistory triggers during bulk operations to avoid performance issues
with pghistory.context(disable=True):
rides = list(Ride.objects.all().select_related(
'park', 'park__location', 'park_area', 'manufacturer', 'designer', 'ride_model'
))
rides = list(
Ride.objects.all().select_related(
"park", "park__location", "park_area", "manufacturer", "designer", "ride_model"
)
)
for ride in rides:
# Extract opening year from opening_date
@@ -39,7 +41,7 @@ def populate_computed_fields(apps, schema_editor):
# Park info
if ride.park:
search_parts.append(ride.park.name)
if hasattr(ride.park, 'location') and ride.park.location:
if hasattr(ride.park, "location") and ride.park.location:
if ride.park.location.city:
search_parts.append(ride.park.location.city)
if ride.park.location.state:
@@ -62,7 +64,7 @@ def populate_computed_fields(apps, schema_editor):
("TR", "Transport"),
("OT", "Other"),
]
category_display = dict(category_choices).get(ride.category, '')
category_display = dict(category_choices).get(ride.category, "")
if category_display:
search_parts.append(category_display)
@@ -79,7 +81,7 @@ def populate_computed_fields(apps, schema_editor):
("DEMOLISHED", "Demolished"),
("RELOCATED", "Relocated"),
]
status_display = dict(status_choices).get(ride.status, '')
status_display = dict(status_choices).get(ride.status, "")
if status_display:
search_parts.append(status_display)
@@ -95,24 +97,24 @@ def populate_computed_fields(apps, schema_editor):
if ride.ride_model.manufacturer:
search_parts.append(ride.ride_model.manufacturer.name)
ride.search_text = ' '.join(filter(None, search_parts)).lower()
ride.search_text = " ".join(filter(None, search_parts)).lower()
# Bulk update all rides
Ride.objects.bulk_update(rides, ['opening_year', 'search_text'], batch_size=1000)
Ride.objects.bulk_update(rides, ["opening_year", "search_text"], batch_size=1000)
def reverse_populate_computed_fields(apps, schema_editor):
"""Clear computed fields (reverse operation)."""
Ride = apps.get_model('rides', 'Ride')
Ride = apps.get_model("rides", "Ride")
# Disable pghistory triggers during bulk operations
with pghistory.context(disable=True):
Ride.objects.all().update(opening_year=None, search_text='')
Ride.objects.all().update(opening_year=None, search_text="")
class Migration(migrations.Migration):
dependencies = [
('rides', '0018_add_hybrid_filtering_fields'),
("rides", "0018_add_hybrid_filtering_fields"),
]
operations = [

View File

@@ -19,163 +19,136 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('rides', '0019_populate_hybrid_filtering_fields'),
("rides", "0019_populate_hybrid_filtering_fields"),
]
operations = [
# Composite index for park + category filtering (very common)
migrations.RunSQL(
"CREATE INDEX rides_ride_park_category_idx ON rides_ride (park_id, category) WHERE category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_category_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_category_idx;",
),
# Composite index for park + status filtering (common)
migrations.RunSQL(
"CREATE INDEX rides_ride_park_status_idx ON rides_ride (park_id, status);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_status_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_status_idx;",
),
# Composite index for category + status filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_category_status_idx ON rides_ride (category, status) WHERE category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_category_status_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_category_status_idx;",
),
# Composite index for manufacturer + category
migrations.RunSQL(
"CREATE INDEX rides_ride_manufacturer_category_idx ON rides_ride (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL AND category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_manufacturer_category_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_manufacturer_category_idx;",
),
# Composite index for opening year + category (for timeline filtering)
migrations.RunSQL(
"CREATE INDEX rides_ride_opening_year_category_idx ON rides_ride (opening_year, category) WHERE opening_year IS NOT NULL AND category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_opening_year_category_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_opening_year_category_idx;",
),
# Partial index for operating rides only (most common filter)
migrations.RunSQL(
"CREATE INDEX rides_ride_operating_only_idx ON rides_ride (park_id, category, opening_year) WHERE status = 'OPERATING';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_operating_only_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_operating_only_idx;",
),
# Partial index for roller coasters only (popular category)
migrations.RunSQL(
"CREATE INDEX rides_ride_roller_coasters_idx ON rides_ride (park_id, status, opening_year) WHERE category = 'RC';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_roller_coasters_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_roller_coasters_idx;",
),
# Covering index for list views (includes commonly displayed fields)
migrations.RunSQL(
"CREATE INDEX rides_ride_list_covering_idx ON rides_ride (park_id, category, status) INCLUDE (name, opening_date, average_rating);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_list_covering_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_list_covering_idx;",
),
# GIN index for full-text search on computed search_text field
migrations.RunSQL(
"CREATE INDEX rides_ride_search_text_gin_idx ON rides_ride USING gin(to_tsvector('english', search_text));",
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_gin_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_gin_idx;",
),
# Trigram index for fuzzy text search
migrations.RunSQL(
"CREATE INDEX rides_ride_search_text_trgm_idx ON rides_ride USING gin(search_text gin_trgm_ops);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_trgm_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_trgm_idx;",
),
# Index for rating-based filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_rating_idx ON rides_ride (average_rating) WHERE average_rating IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_rating_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_rating_idx;",
),
# Index for capacity-based filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_capacity_idx ON rides_ride (capacity_per_hour) WHERE capacity_per_hour IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_capacity_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_capacity_idx;",
),
# Index for height requirement filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_height_req_idx ON rides_ride (min_height_in, max_height_in) WHERE min_height_in IS NOT NULL OR max_height_in IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_height_req_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_height_req_idx;",
),
# Composite index for ride model filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_model_manufacturer_idx ON rides_ride (ride_model_id, manufacturer_id) WHERE ride_model_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_model_manufacturer_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_model_manufacturer_idx;",
),
# Index for designer filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_designer_idx ON rides_ride (designer_id, category) WHERE designer_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_designer_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_designer_idx;",
),
# Index for park area filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_park_area_idx ON rides_ride (park_area_id, status) WHERE park_area_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_area_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_area_idx;",
),
# Roller coaster stats indexes for performance
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_height_idx ON rides_rollercoasterstats (height_ft) WHERE height_ft IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_height_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_height_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_speed_idx ON rides_rollercoasterstats (speed_mph) WHERE speed_mph IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_speed_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_speed_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_inversions_idx ON rides_rollercoasterstats (inversions);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_inversions_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_inversions_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_type_material_idx ON rides_rollercoasterstats (roller_coaster_type, track_material);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_type_material_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_type_material_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_launch_type_idx ON rides_rollercoasterstats (launch_type);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_launch_type_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_launch_type_idx;",
),
# Composite index for complex roller coaster filtering
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_complex_idx ON rides_rollercoasterstats (roller_coaster_type, track_material, launch_type) INCLUDE (height_ft, speed_mph, inversions);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_complex_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_complex_idx;",
),
# Index for ride model filtering and search
migrations.RunSQL(
"CREATE INDEX rides_ridemodel_manufacturer_category_idx ON rides_ridemodel (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_manufacturer_category_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_manufacturer_category_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_ridemodel_name_trgm_idx ON rides_ridemodel USING gin(name gin_trgm_ops);",
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_name_trgm_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_name_trgm_idx;",
),
# Index for company role-based filtering
migrations.RunSQL(
"CREATE INDEX rides_company_manufacturer_role_idx ON rides_company USING gin(roles) WHERE 'MANUFACTURER' = ANY(roles);",
reverse_sql="DROP INDEX IF EXISTS rides_company_manufacturer_role_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_company_manufacturer_role_idx;",
),
migrations.RunSQL(
"CREATE INDEX rides_company_designer_role_idx ON rides_company USING gin(roles) WHERE 'DESIGNER' = ANY(roles);",
reverse_sql="DROP INDEX IF EXISTS rides_company_designer_role_idx;"
reverse_sql="DROP INDEX IF EXISTS rides_company_designer_role_idx;",
),
# Ensure trigram extension is available for fuzzy search
migrations.RunSQL(
"CREATE EXTENSION IF NOT EXISTS pg_trgm;",
reverse_sql="-- Cannot safely drop pg_trgm extension"
"CREATE EXTENSION IF NOT EXISTS pg_trgm;", reverse_sql="-- Cannot safely drop pg_trgm extension"
),
]

View File

@@ -11,30 +11,30 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rides', '0025_convert_ride_status_to_fsm'),
("rides", "0025_convert_ride_status_to_fsm"),
]
operations = [
# Remove the old unique_together constraint
migrations.AlterUniqueTogether(
name='ridemodel',
name="ridemodel",
unique_together=set(),
),
# Add new UniqueConstraints with better error messages
migrations.AddConstraint(
model_name='ridemodel',
model_name="ridemodel",
constraint=models.UniqueConstraint(
fields=['manufacturer', 'name'],
name='ridemodel_manufacturer_name_unique',
violation_error_message='A ride model with this name already exists for this manufacturer'
fields=["manufacturer", "name"],
name="ridemodel_manufacturer_name_unique",
violation_error_message="A ride model with this name already exists for this manufacturer",
),
),
migrations.AddConstraint(
model_name='ridemodel',
model_name="ridemodel",
constraint=models.UniqueConstraint(
fields=['manufacturer', 'slug'],
name='ridemodel_manufacturer_slug_unique',
violation_error_message='A ride model with this slug already exists for this manufacturer'
fields=["manufacturer", "slug"],
name="ridemodel_manufacturer_slug_unique",
violation_error_message="A ride model with this slug already exists for this manufacturer",
),
),
]

View File

@@ -98,23 +98,17 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="company",
name="coasters_count",
field=models.IntegerField(
default=0, help_text="Number of coasters manufactured (auto-calculated)"
),
field=models.IntegerField(default=0, help_text="Number of coasters manufactured (auto-calculated)"),
),
migrations.AlterField(
model_name="company",
name="description",
field=models.TextField(
blank=True, help_text="Detailed company description"
),
field=models.TextField(blank=True, help_text="Detailed company description"),
),
migrations.AlterField(
model_name="company",
name="founded_date",
field=models.DateField(
blank=True, help_text="Date the company was founded", null=True
),
field=models.DateField(blank=True, help_text="Date the company was founded", null=True),
),
migrations.AlterField(
model_name="company",
@@ -124,9 +118,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="company",
name="rides_count",
field=models.IntegerField(
default=0, help_text="Number of rides manufactured (auto-calculated)"
),
field=models.IntegerField(default=0, help_text="Number of rides manufactured (auto-calculated)"),
),
migrations.AlterField(
model_name="company",
@@ -151,9 +143,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="company",
name="slug",
field=models.SlugField(
help_text="URL-friendly identifier", max_length=255, unique=True
),
field=models.SlugField(help_text="URL-friendly identifier", max_length=255, unique=True),
),
migrations.AlterField(
model_name="company",
@@ -163,23 +153,17 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="companyevent",
name="coasters_count",
field=models.IntegerField(
default=0, help_text="Number of coasters manufactured (auto-calculated)"
),
field=models.IntegerField(default=0, help_text="Number of coasters manufactured (auto-calculated)"),
),
migrations.AlterField(
model_name="companyevent",
name="description",
field=models.TextField(
blank=True, help_text="Detailed company description"
),
field=models.TextField(blank=True, help_text="Detailed company description"),
),
migrations.AlterField(
model_name="companyevent",
name="founded_date",
field=models.DateField(
blank=True, help_text="Date the company was founded", null=True
),
field=models.DateField(blank=True, help_text="Date the company was founded", null=True),
),
migrations.AlterField(
model_name="companyevent",
@@ -210,9 +194,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="companyevent",
name="rides_count",
field=models.IntegerField(
default=0, help_text="Number of rides manufactured (auto-calculated)"
),
field=models.IntegerField(default=0, help_text="Number of rides manufactured (auto-calculated)"),
),
migrations.AlterField(
model_name="companyevent",
@@ -237,9 +219,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="companyevent",
name="slug",
field=models.SlugField(
db_index=False, help_text="URL-friendly identifier", max_length=255
),
field=models.SlugField(db_index=False, help_text="URL-friendly identifier", max_length=255),
),
migrations.AlterField(
model_name="companyevent",
@@ -321,23 +301,17 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelphoto",
name="caption",
field=models.CharField(
blank=True, help_text="Photo caption or description", max_length=500
),
field=models.CharField(blank=True, help_text="Photo caption or description", max_length=500),
),
migrations.AlterField(
model_name="ridemodelphoto",
name="copyright_info",
field=models.CharField(
blank=True, help_text="Copyright information", max_length=255
),
field=models.CharField(blank=True, help_text="Copyright information", max_length=255),
),
migrations.AlterField(
model_name="ridemodelphoto",
name="photographer",
field=models.CharField(
blank=True, help_text="Name of the photographer", max_length=255
),
field=models.CharField(blank=True, help_text="Name of the photographer", max_length=255),
),
migrations.AlterField(
model_name="ridemodelphoto",
@@ -352,9 +326,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelphoto",
name="source",
field=models.CharField(
blank=True, help_text="Source of the photo", max_length=255
),
field=models.CharField(blank=True, help_text="Source of the photo", max_length=255),
),
migrations.AlterField(
model_name="ridemodelphotoevent",
@@ -368,16 +340,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelphotoevent",
name="caption",
field=models.CharField(
blank=True, help_text="Photo caption or description", max_length=500
),
field=models.CharField(blank=True, help_text="Photo caption or description", max_length=500),
),
migrations.AlterField(
model_name="ridemodelphotoevent",
name="copyright_info",
field=models.CharField(
blank=True, help_text="Copyright information", max_length=255
),
field=models.CharField(blank=True, help_text="Copyright information", max_length=255),
),
migrations.AlterField(
model_name="ridemodelphotoevent",
@@ -403,9 +371,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelphotoevent",
name="photographer",
field=models.CharField(
blank=True, help_text="Name of the photographer", max_length=255
),
field=models.CharField(blank=True, help_text="Name of the photographer", max_length=255),
),
migrations.AlterField(
model_name="ridemodelphotoevent",
@@ -422,9 +388,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="ridemodelphotoevent",
name="source",
field=models.CharField(
blank=True, help_text="Source of the photo", max_length=255
),
field=models.CharField(blank=True, help_text="Source of the photo", max_length=255),
),
migrations.AlterField(
model_name="ridemodeltechnicalspec",
@@ -709,9 +673,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstats",
name="cars_per_train",
field=models.PositiveIntegerField(
blank=True, help_text="Number of cars per train", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of cars per train", null=True),
),
migrations.AlterField(
model_name="rollercoasterstats",
@@ -727,9 +689,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstats",
name="inversions",
field=models.PositiveIntegerField(
default=0, help_text="Number of inversions"
),
field=models.PositiveIntegerField(default=0, help_text="Number of inversions"),
),
migrations.AlterField(
model_name="rollercoasterstats",
@@ -766,16 +726,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstats",
name="ride_time_seconds",
field=models.PositiveIntegerField(
blank=True, help_text="Duration of the ride in seconds", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Duration of the ride in seconds", null=True),
),
migrations.AlterField(
model_name="rollercoasterstats",
name="seats_per_car",
field=models.PositiveIntegerField(
blank=True, help_text="Number of seats per car", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of seats per car", null=True),
),
migrations.AlterField(
model_name="rollercoasterstats",
@@ -809,16 +765,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstats",
name="trains_count",
field=models.PositiveIntegerField(
blank=True, help_text="Number of trains", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of trains", null=True),
),
migrations.AlterField(
model_name="rollercoasterstatsevent",
name="cars_per_train",
field=models.PositiveIntegerField(
blank=True, help_text="Number of cars per train", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of cars per train", null=True),
),
migrations.AlterField(
model_name="rollercoasterstatsevent",
@@ -834,9 +786,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstatsevent",
name="inversions",
field=models.PositiveIntegerField(
default=0, help_text="Number of inversions"
),
field=models.PositiveIntegerField(default=0, help_text="Number of inversions"),
),
migrations.AlterField(
model_name="rollercoasterstatsevent",
@@ -896,16 +846,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstatsevent",
name="ride_time_seconds",
field=models.PositiveIntegerField(
blank=True, help_text="Duration of the ride in seconds", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Duration of the ride in seconds", null=True),
),
migrations.AlterField(
model_name="rollercoasterstatsevent",
name="seats_per_car",
field=models.PositiveIntegerField(
blank=True, help_text="Number of seats per car", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of seats per car", null=True),
),
migrations.AlterField(
model_name="rollercoasterstatsevent",
@@ -939,8 +885,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="rollercoasterstatsevent",
name="trains_count",
field=models.PositiveIntegerField(
blank=True, help_text="Number of trains", null=True
),
field=models.PositiveIntegerField(blank=True, help_text="Number of trains", null=True),
),
]