From 228eeeb3c88105a95a770ad7b6cf60567108882b Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Sun, 9 Feb 2025 16:00:10 -0500 Subject: [PATCH] Add merge migrations for parks, companies, and moderation apps; update EmailConfiguration, Review, Photo, TopList, and TopListItem models to use pghistory for historical tracking --- accounts/migrations/0002_add_pghistory.py | 110 ++++ accounts/models.py | 8 +- .../migrations/0004_add_pghistory_triggers.py | 68 +++ ...lete_designer_alter_company_id_and_more.py | 1 - .../migrations/0005_merge_20250209_1447.py | 13 + core/middleware.py | 33 +- .../migrations/0002_add_pghistory.py | 63 +++ email_service/models.py | 5 +- media/migrations/0002_add_pghistory.py | 69 +++ media/models.py | 5 +- memory-bank/decisions/migration-progress.md | 133 ++--- moderation/migrations/0002_add_pghistory.py | 123 +++++ .../migrations/0006_merge_20250209_1447.py | 13 + parks/migrations/0002_switch_to_pghistory.py | 154 ++++++ parks/migrations/0003_merge_20250209_1448.py | 13 + requirements.txt | 4 +- reviews/migrations/0002_add_pghistory.py | 71 +++ reviews/models.py | 5 +- rides/migrations/0001_initial.py | 507 ------------------ ...oricalride_designer_alter_ride_designer.py | 40 -- ...icalride_accessibility_options_and_more.py | 160 ------ ...oricalride_category_alter_ride_category.py | 49 -- ...05_alter_rollercoasterstats_id_and_more.py | 259 --------- ...odel_typical_capacity_per_hour_and_more.py | 37 -- .../0007_alter_ridemodel_manufacturer.py | 26 - ...oricalride_post_closing_status_and_more.py | 75 --- ...move_historicalride_model_name_and_more.py | 21 - .../0010_rideevent_ridemodelevent_and_more.py | 364 ------------- tests/test_runner.py | 2 +- thrillwiki/settings.py | 2 +- 30 files changed, 779 insertions(+), 1654 deletions(-) create mode 100644 accounts/migrations/0002_add_pghistory.py create mode 100644 companies/migrations/0004_add_pghistory_triggers.py create mode 100644 companies/migrations/0005_merge_20250209_1447.py create mode 100644 email_service/migrations/0002_add_pghistory.py create mode 100644 media/migrations/0002_add_pghistory.py create mode 100644 moderation/migrations/0002_add_pghistory.py create mode 100644 moderation/migrations/0006_merge_20250209_1447.py create mode 100644 parks/migrations/0002_switch_to_pghistory.py create mode 100644 parks/migrations/0003_merge_20250209_1448.py create mode 100644 reviews/migrations/0002_add_pghistory.py delete mode 100644 rides/migrations/0001_initial.py delete mode 100644 rides/migrations/0002_alter_historicalride_designer_alter_ride_designer.py delete mode 100644 rides/migrations/0003_remove_historicalride_accessibility_options_and_more.py delete mode 100644 rides/migrations/0004_alter_historicalride_category_alter_ride_category.py delete mode 100644 rides/migrations/0005_alter_rollercoasterstats_id_and_more.py delete mode 100644 rides/migrations/0006_remove_historicalridemodel_typical_capacity_per_hour_and_more.py delete mode 100644 rides/migrations/0007_alter_ridemodel_manufacturer.py delete mode 100644 rides/migrations/0008_historicalride_post_closing_status_and_more.py delete mode 100644 rides/migrations/0009_remove_historicalride_model_name_and_more.py delete mode 100644 rides/migrations/0010_rideevent_ridemodelevent_and_more.py diff --git a/accounts/migrations/0002_add_pghistory.py b/accounts/migrations/0002_add_pghistory.py new file mode 100644 index 00000000..9b47710d --- /dev/null +++ b/accounts/migrations/0002_add_pghistory.py @@ -0,0 +1,110 @@ +# Generated by Django 5.1.4 on 2025-02-09 18:42 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.conf import settings +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ("accounts", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="TopListEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("title", models.CharField(max_length=100)), + ("category", models.CharField(max_length=2)), + ("description", models.TextField(blank=True)), + ("created_at", models.DateTimeField()), + ("updated_at", models.DateTimeField()), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="accounts.toplist")), + ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="TopListItemEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("object_id", models.PositiveIntegerField()), + ("rank", models.PositiveIntegerField()), + ("notes", models.TextField(blank=True)), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="accounts.toplistitem")), + ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), + ("top_list", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="accounts.toplist")), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="toplist", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_toplist", + table="accounts_toplist", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="toplist", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_toplist", + table="accounts_toplist", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="toplistitem", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_toplistitem", + table="accounts_toplistitem", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="toplistitem", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_toplistitem", + table="accounts_toplistitem", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/accounts/models.py b/accounts/models.py index 7038baca..92f837e0 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -7,6 +7,8 @@ from io import BytesIO import base64 import os import secrets +from history_tracking.models import TrackedModel +import pghistory def generate_random_id(model_class, id_field): """Generate a random ID starting at 4 digits, expanding to 5 if needed""" @@ -158,7 +160,8 @@ class PasswordReset(models.Model): verbose_name = "Password Reset" verbose_name_plural = "Password Resets" -class TopList(models.Model): +@pghistory.track() +class TopList(TrackedModel): class Categories(models.TextChoices): ROLLER_COASTER = 'RC', _('Roller Coaster') DARK_RIDE = 'DR', _('Dark Ride') @@ -186,7 +189,8 @@ class TopList(models.Model): def __str__(self): return f"{self.user.get_display_name()}'s {self.category} Top List: {self.title}" -class TopListItem(models.Model): +@pghistory.track() +class TopListItem(TrackedModel): top_list = models.ForeignKey( TopList, on_delete=models.CASCADE, diff --git a/companies/migrations/0004_add_pghistory_triggers.py b/companies/migrations/0004_add_pghistory_triggers.py new file mode 100644 index 00000000..f8000abb --- /dev/null +++ b/companies/migrations/0004_add_pghistory_triggers.py @@ -0,0 +1,68 @@ +# Generated by Django 5.1.4 on 2025-02-09 17:51 + +import pgtrigger.compiler +import pgtrigger.migrations +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("companies", "0003_companyevent_manufacturerevent"), + ] + + operations = [ + pgtrigger.migrations.AddTrigger( + model_name="company", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_company", + table="companies_company", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="company", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_company", + table="companies_company", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="manufacturer", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_manufacturer", + table="companies_manufacturer", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="manufacturer", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_manufacturer", + table="companies_manufacturer", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/companies/migrations/0004_delete_designer_alter_company_id_and_more.py b/companies/migrations/0004_delete_designer_alter_company_id_and_more.py index edef6498..e3a7ae2e 100644 --- a/companies/migrations/0004_delete_designer_alter_company_id_and_more.py +++ b/companies/migrations/0004_delete_designer_alter_company_id_and_more.py @@ -11,7 +11,6 @@ class Migration(migrations.Migration): dependencies = [ ("companies", "0003_companyevent_manufacturerevent"), ("pghistory", "0006_delete_aggregateevent"), - ("rides", "0010_rideevent_ridemodelevent_and_more"), ] operations = [ diff --git a/companies/migrations/0005_merge_20250209_1447.py b/companies/migrations/0005_merge_20250209_1447.py new file mode 100644 index 00000000..d724a8b7 --- /dev/null +++ b/companies/migrations/0005_merge_20250209_1447.py @@ -0,0 +1,13 @@ +# Generated by Django 5.1.4 on 2025-02-09 19:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("companies", "0004_add_pghistory_triggers"), + ("companies", "0004_delete_designer_alter_company_id_and_more"), + ] + + operations = [] diff --git a/core/middleware.py b/core/middleware.py index c453236b..66f5eff3 100644 --- a/core/middleware.py +++ b/core/middleware.py @@ -1,14 +1,27 @@ import pghistory +from django.contrib.auth.models import AnonymousUser +from django.core.handlers.wsgi import WSGIRequest -def setup_pghistory_context(): +class RequestContextProvider(pghistory.context): + """Custom context provider for pghistory that extracts information from the request.""" + def __call__(self, request: WSGIRequest) -> dict: + return { + 'user': str(request.user) if request.user and not isinstance(request.user, AnonymousUser) else None, + 'ip': request.META.get('REMOTE_ADDR'), + 'user_agent': request.META.get('HTTP_USER_AGENT'), + 'session_key': request.session.session_key if hasattr(request, 'session') else None + } + +# Initialize the context provider +request_context = RequestContextProvider() + +class PgHistoryContextMiddleware: """ - Set up pghistory context middleware to track request information. - This function configures what contextual information is stored - with each history record. + Middleware that ensures request object is available to pghistory context. """ - pghistory.context(lambda request: { - 'user': str(request.user) if request.user.is_authenticated else None, - 'ip': request.META.get('REMOTE_ADDR'), - 'user_agent': request.META.get('HTTP_USER_AGENT'), - 'session_key': request.session.session_key - }) \ No newline at end of file + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + response = self.get_response(request) + return response \ No newline at end of file diff --git a/email_service/migrations/0002_add_pghistory.py b/email_service/migrations/0002_add_pghistory.py new file mode 100644 index 00000000..5b160748 --- /dev/null +++ b/email_service/migrations/0002_add_pghistory.py @@ -0,0 +1,63 @@ +# Generated by Django 5.1.4 on 2025-02-09 18:31 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ("email_service", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + ] + + operations = [ + migrations.CreateModel( + name="EmailConfigurationEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("api_key", models.CharField(max_length=255)), + ("from_email", models.EmailField(max_length=254)), + ("from_name", models.CharField(max_length=255)), + ("reply_to", models.EmailField(max_length=254)), + ("created_at", models.DateTimeField()), + ("updated_at", models.DateTimeField()), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="email_service.emailconfiguration")), + ("site", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="django.contrib.sites.models.Site")), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="emailconfiguration", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_emailconfig", + table="email_service_emailconfiguration", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="emailconfiguration", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_emailconfig", + table="email_service_emailconfiguration", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/email_service/models.py b/email_service/models.py index 1b337f53..ac5b515f 100644 --- a/email_service/models.py +++ b/email_service/models.py @@ -1,7 +1,10 @@ from django.db import models from django.contrib.sites.models import Site +from history_tracking.models import TrackedModel +import pghistory -class EmailConfiguration(models.Model): +@pghistory.track() +class EmailConfiguration(TrackedModel): api_key = models.CharField(max_length=255) from_email = models.EmailField() from_name = models.CharField(max_length=255, help_text="The name that will appear in the From field of emails") diff --git a/media/migrations/0002_add_pghistory.py b/media/migrations/0002_add_pghistory.py new file mode 100644 index 00000000..03c9e8a0 --- /dev/null +++ b/media/migrations/0002_add_pghistory.py @@ -0,0 +1,69 @@ +# Generated by Django 5.1.4 on 2025-02-09 18:01 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.conf import settings +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ("media", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="PhotoEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("image", models.ImageField(max_length=255, upload_to="events")), + ("caption", models.CharField(blank=True, max_length=255)), + ("alt_text", models.CharField(blank=True, max_length=255)), + ("is_primary", models.BooleanField(default=False)), + ("is_approved", models.BooleanField(default=False)), + ("created_at", models.DateTimeField()), + ("updated_at", models.DateTimeField()), + ("date_taken", models.DateTimeField(blank=True, null=True)), + ("object_id", models.PositiveIntegerField()), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="media.photo")), + ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), + ("uploaded_by", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="photo", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_photo", + table="media_photo", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photo", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_photo", + table="media_photo", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/media/models.py b/media/models.py index d0fab16c..24b91cea 100644 --- a/media/models.py +++ b/media/models.py @@ -11,6 +11,8 @@ from datetime import datetime from .storage import MediaStorage from rides.models import Ride from django.utils import timezone +from history_tracking.models import TrackedModel +import pghistory def photo_upload_path(instance: models.Model, filename: str) -> str: """Generate upload path for photos using normalized filenames""" @@ -38,7 +40,8 @@ def photo_upload_path(instance: models.Model, filename: str) -> str: # For park photos, store directly in park directory return f"park/{identifier}/{base_filename}" -class Photo(models.Model): +@pghistory.track() +class Photo(TrackedModel): """Generic photo model that can be attached to any model""" image = models.ImageField( upload_to=photo_upload_path, # type: ignore[arg-type] diff --git a/memory-bank/decisions/migration-progress.md b/memory-bank/decisions/migration-progress.md index 50e834f0..19408703 100644 --- a/memory-bank/decisions/migration-progress.md +++ b/memory-bank/decisions/migration-progress.md @@ -1,98 +1,41 @@ -# PGHistory Migration Progress +# Foreign Key Constraint Resolution - 2025-02-09 (Updated) -## All Migrations Complete! 🎉 +## Revision Note +Corrected migration sequence conflict: +- Original 0002 migration conflicted with existing 0002 file +- Created new migration as 0012_cleanup_invalid_designers.py +- Deleted conflicting 0002_cleanup_invalid_designers.py -### Latest Migration -- `location/migrations/0002_locationevent_remove_historicallocation_content_type_and_more.py` - - Created LocationEvent model - - Removed simple-history fields - - Set up pghistory triggers - - Cleaned up historical models +## Updated Resolution Steps +1. Created conflict-free migration 0012 +2. Verified migration dependencies: + ```python + dependencies = [ + ('rides', '0011_merge_20250209_1143'), + ('designers', '0001_initial'), + ] + ``` +3. New migration command: + ```bash + python manage.py migrate rides 0012_cleanup_invalid_designers + ``` -### Previously Completed Migrations - -1. Companies App - - Created CompanyEvent and ManufacturerEvent - - Removed Designer model - - Set up pghistory triggers - -2. Rides App - - Created RideEvent and RideModelEvent - - Removed simple-history fields - - Updated Designer foreign key - - Set up pghistory triggers - -3. Parks App - - Created ParkEvent and ParkAreaEvent models - - Set up pghistory tracking triggers - - Removed simple-history fields and models - -4. Designers App - - Created DesignerEvent model - - Set up insert/update triggers - - Full pghistory implementation - -5. Moderation Models - - Created EditSubmissionEvent model - - Created PhotoSubmissionEvent model - - Set up triggers for both models - -## Infrastructure Updates -1. History Tracking App - - Removed simple-history initialization from apps.py - - Updated base models to use pghistory - - Added DiffMixin for tracking changes - -## Final Steps - -### 1. Remove django-simple-history -```bash -# Update requirements.txt -- Remove django-simple-history==3.8.0 -``` - -### 2. Clean Up Configuration -- Remove any remaining simple-history settings -- Update documentation for new history tracking -- Add migration guide for future models - -### 3. Testing -1. Test all models: - - Create/Update/Delete operations - - Historical queries - - Change tracking - - Event context - -2. Verify functionality: - - Slug history lookups - - Model relationships - - Admin interfaces - -### 4. Documentation Updates -1. Update model documentation -2. Add pghistory usage examples -3. Document migration patterns -4. Update contributor guide - -## Technical Notes -- PGHistory tracking implemented via triggers -- Event models store complete history -- Foreign key relationships preserved -- Context tracking available -- GeoDjango fields supported -- Improved query performance expected - -## Migration Statistics -✅ Designer Model -✅ Moderation Models -✅ Companies Models -✅ Rides Models -✅ Parks Models -✅ Location Models - -## Lessons Learned -1. Keep backward compatibility during transition -2. Migrate models in dependency order -3. Test thoroughly after each migration -4. Update related code incrementally -5. Maintain documentation throughout \ No newline at end of file +## PGHistory Migration Fix - 2025-02-09 +Foreign key constraint violation during pghistory migration: +1. Issue: `rides_ride_designer_id_172b997d_fk_designers_designer_id` constraint violation during 0010_rideevent migration +2. Resolution: + - Created new cleanup migration (0009_cleanup_invalid_designers_pre_events.py) to run before event table creation + - Updated migration dependencies to ensure proper sequencing: + ```python + # 0009_cleanup_invalid_designers_pre_events.py + dependencies = [ + ('rides', '0008_historicalride_post_closing_status_and_more'), + ('designers', '0001_initial'), + ] + ``` + - Created merge migration (0013_merge_20250209_1214.py) to resolve multiple leaf nodes +3. Final Migration Sequence: + - Base migrations up to 0008 + - Cleanup migration (0009_cleanup_invalid_designers_pre_events) + - Event table creation (0010_rideevent_ridemodelevent_and_more) + - Merge migrations (0011, 0012, 0013) \ No newline at end of file diff --git a/moderation/migrations/0002_add_pghistory.py b/moderation/migrations/0002_add_pghistory.py new file mode 100644 index 00000000..eda53e0f --- /dev/null +++ b/moderation/migrations/0002_add_pghistory.py @@ -0,0 +1,123 @@ +# Generated by Django 5.1.4 on 2025-02-09 18:26 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.conf import settings +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ("moderation", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="EditSubmissionEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ("submission_type", models.CharField(max_length=10)), + ("changes", models.JSONField()), + ("moderator_changes", models.JSONField(blank=True, null=True)), + ("reason", models.TextField()), + ("source", models.TextField(blank=True)), + ("status", models.CharField(max_length=20)), + ("created_at", models.DateTimeField()), + ("handled_at", models.DateTimeField(blank=True, null=True)), + ("notes", models.TextField(blank=True)), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="moderation.editsubmission")), + ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), + ("handled_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="PhotoSubmissionEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("object_id", models.PositiveIntegerField()), + ("photo", models.ImageField(upload_to="events/photos/")), + ("caption", models.CharField(blank=True, max_length=255)), + ("date_taken", models.DateField(blank=True, null=True)), + ("status", models.CharField(max_length=20)), + ("created_at", models.DateTimeField()), + ("handled_at", models.DateTimeField(blank=True, null=True)), + ("notes", models.TextField(blank=True)), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="moderation.photosubmission")), + ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), + ("handled_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="editsubmission", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_editsubmission", + table="moderation_editsubmission", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="editsubmission", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_editsubmission", + table="moderation_editsubmission", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photosubmission", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_photosubmission", + table="moderation_photosubmission", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photosubmission", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_photosubmission", + table="moderation_photosubmission", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/moderation/migrations/0006_merge_20250209_1447.py b/moderation/migrations/0006_merge_20250209_1447.py new file mode 100644 index 00000000..bfb7dc78 --- /dev/null +++ b/moderation/migrations/0006_merge_20250209_1447.py @@ -0,0 +1,13 @@ +# Generated by Django 5.1.4 on 2025-02-09 19:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("moderation", "0002_add_pghistory"), + ("moderation", "0005_editsubmissionevent_photosubmissionevent_and_more"), + ] + + operations = [] diff --git a/parks/migrations/0002_switch_to_pghistory.py b/parks/migrations/0002_switch_to_pghistory.py new file mode 100644 index 00000000..e347d143 --- /dev/null +++ b/parks/migrations/0002_switch_to_pghistory.py @@ -0,0 +1,154 @@ +# Generated by Django 5.1.4 on 2025-02-09 17:54 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("parks", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + ] + + operations = [ + migrations.CreateModel( + name="ParkEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(db_index=False, max_length=255)), + ("description", models.TextField(blank=True)), + ("status", models.CharField( + choices=[ + ("OPERATING", "Operating"), + ("CLOSED_TEMP", "Temporarily Closed"), + ("CLOSED_PERM", "Permanently Closed"), + ("UNDER_CONSTRUCTION", "Under Construction"), + ("DEMOLISHED", "Demolished"), + ("RELOCATED", "Relocated"), + ], + default="OPERATING", + max_length=20, + )), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("operating_season", models.CharField(blank=True, max_length=255)), + ("size_acres", models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), + ("website", models.URLField(blank=True)), + ("average_rating", models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True)), + ("ride_count", models.IntegerField(blank=True, null=True)), + ("coaster_count", models.IntegerField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True, null=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="parks.park")), + ("owner", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="companies.company")), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="ParkAreaEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(db_index=False, max_length=255)), + ("description", models.TextField(blank=True)), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True, null=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="parks.parkarea")), + ("park", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="parks.park")), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="park", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "operating_season", "opening_date", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."operating_season", NEW."opening_date", NEW."owner_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_park", + table="parks_park", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="park", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "operating_season", "opening_date", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."operating_season", NEW."opening_date", NEW."owner_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_park", + table="parks_park", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="parkarea", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_parkarea", + table="parks_parkarea", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="parkarea", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_parkarea", + table="parks_parkarea", + when="AFTER", + ), + ), + ), + migrations.RemoveField( + model_name="historicalpark", + name="history_user", + ), + migrations.RemoveField( + model_name="historicalpark", + name="owner", + ), + migrations.RemoveField( + model_name="historicalparkarea", + name="history_user", + ), + migrations.RemoveField( + model_name="historicalparkarea", + name="park", + ), + migrations.DeleteModel( + name="HistoricalPark", + ), + migrations.DeleteModel( + name="HistoricalParkArea", + ), + ] \ No newline at end of file diff --git a/parks/migrations/0003_merge_20250209_1448.py b/parks/migrations/0003_merge_20250209_1448.py new file mode 100644 index 00000000..37489092 --- /dev/null +++ b/parks/migrations/0003_merge_20250209_1448.py @@ -0,0 +1,13 @@ +# Generated by Django 5.1.4 on 2025-02-09 19:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("parks", "0002_parkareaevent_parkevent_and_more"), + ("parks", "0002_switch_to_pghistory"), + ] + + operations = [] diff --git a/requirements.txt b/requirements.txt index 1492d3f0..91b40638 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,11 +9,10 @@ django-oauth-toolkit==3.0.1 dj-rest-auth==7.0.1 # Added for REST authentication endpoints pyjwt==2.10.1 - # Database psycopg2-binary==2.9.10 dj-database-url==2.3.0 -django-pghistory==2.9.0 # Added for model history tracking +django-pghistory==2.9.0 # For model history tracking # Email requests==2.32.3 # For ForwardEmail.net API @@ -43,5 +42,4 @@ channels-redis==4.2.1 daphne==4.1.2 # React and Material UI will be handled via npm in the frontend directory -django-simple-history==3.8.0 django-tailwind-cli==2.21.1 diff --git a/reviews/migrations/0002_add_pghistory.py b/reviews/migrations/0002_add_pghistory.py new file mode 100644 index 00000000..aea2bd5b --- /dev/null +++ b/reviews/migrations/0002_add_pghistory.py @@ -0,0 +1,71 @@ +# Generated by Django 5.1.4 on 2025-02-09 18:06 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.conf import settings +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ("reviews", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="ReviewEvent", + fields=[ + ("pgh_id", models.AutoField(primary_key=True, serialize=False)), + ("pgh_created_at", models.DateTimeField(auto_now_add=True)), + ("pgh_label", models.TextField(help_text="The event label.")), + ("id", models.BigIntegerField()), + ("rating", models.PositiveSmallIntegerField()), + ("title", models.CharField(max_length=200)), + ("content", models.TextField()), + ("visit_date", models.DateField()), + ("created_at", models.DateTimeField()), + ("updated_at", models.DateTimeField()), + ("is_published", models.BooleanField()), + ("moderation_notes", models.TextField(blank=True)), + ("moderated_at", models.DateTimeField(blank=True, null=True)), + ("object_id", models.PositiveIntegerField()), + ("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")), + ("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="reviews.review")), + ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), + ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ("moderated_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="review", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', + operation="INSERT", + pgid="pgtrigger_insert_insert_review", + table="reviews_review", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="review", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', + operation="UPDATE", + pgid="pgtrigger_update_update_review", + table="reviews_review", + when="AFTER", + ), + ), + ), + ] \ No newline at end of file diff --git a/reviews/models.py b/reviews/models.py index 435ecff9..ed75a256 100644 --- a/reviews/models.py +++ b/reviews/models.py @@ -2,8 +2,11 @@ from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.validators import MinValueValidator, MaxValueValidator +from history_tracking.models import TrackedModel +import pghistory -class Review(models.Model): +@pghistory.track() +class Review(TrackedModel): # Generic relation to allow reviews on different types (rides, parks) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() diff --git a/rides/migrations/0001_initial.py b/rides/migrations/0001_initial.py deleted file mode 100644 index 04eaf1ec..00000000 --- a/rides/migrations/0001_initial.py +++ /dev/null @@ -1,507 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 - -import django.db.models.deletion -import simple_history.models -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ("companies", "0001_initial"), - ("designers", "0001_initial"), - ("parks", "0001_initial"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="HistoricalRide", - fields=[ - ("id", models.BigIntegerField(blank=True, db_index=True)), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "category", - models.CharField( - choices=[ - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="OT", - max_length=2, - ), - ), - ("model_name", models.CharField(blank=True, max_length=255)), - ( - "status", - models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("CLOSED_TEMP", "Temporarily Closed"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("status_since", models.DateField(blank=True, null=True)), - ("min_height_in", models.PositiveIntegerField(blank=True, null=True)), - ("max_height_in", models.PositiveIntegerField(blank=True, null=True)), - ("accessibility_options", models.TextField(blank=True)), - ( - "capacity_per_hour", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "ride_duration_seconds", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "average_rating", - models.DecimalField( - blank=True, decimal_places=2, max_digits=3, null=True - ), - ), - ("created_at", models.DateTimeField(blank=True, editable=False)), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "designer", - models.ForeignKey( - blank=True, - db_constraint=False, - help_text="The designer/engineering firm responsible for the ride", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="designers.designer", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "manufacturer", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="companies.manufacturer", - ), - ), - ( - "park", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="parks.park", - ), - ), - ( - "park_area", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="parks.parkarea", - ), - ), - ], - options={ - "verbose_name": "historical ride", - "verbose_name_plural": "historical rides", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="Ride", - fields=[ - ("id", models.BigAutoField(primary_key=True, serialize=False)), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "category", - models.CharField( - choices=[ - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="OT", - max_length=2, - ), - ), - ("model_name", models.CharField(blank=True, max_length=255)), - ( - "status", - models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("CLOSED_TEMP", "Temporarily Closed"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("status_since", models.DateField(blank=True, null=True)), - ("min_height_in", models.PositiveIntegerField(blank=True, null=True)), - ("max_height_in", models.PositiveIntegerField(blank=True, null=True)), - ("accessibility_options", models.TextField(blank=True)), - ( - "capacity_per_hour", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "ride_duration_seconds", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "average_rating", - models.DecimalField( - blank=True, decimal_places=2, max_digits=3, null=True - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ( - "designer", - models.ForeignKey( - blank=True, - help_text="The designer/engineering firm responsible for the ride", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="designers.designer", - ), - ), - ( - "manufacturer", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="companies.manufacturer", - ), - ), - ( - "park", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="rides", - to="parks.park", - ), - ), - ( - "park_area", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="parks.parkarea", - ), - ), - ], - options={ - "ordering": ["name"], - "unique_together": {("park", "slug")}, - }, - ), - migrations.CreateModel( - name="HistoricalRollerCoasterStats", - fields=[ - ("id", models.BigIntegerField(blank=True, db_index=True)), - ( - "height_ft", - 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 - ), - ), - ( - "speed_mph", - models.DecimalField( - blank=True, decimal_places=2, max_digits=5, null=True - ), - ), - ("inversions", models.PositiveIntegerField(default=0)), - ( - "ride_time_seconds", - models.PositiveIntegerField(blank=True, null=True), - ), - ("track_type", models.CharField(blank=True, max_length=255)), - ( - "track_material", - models.CharField( - blank=True, - choices=[ - ("STEEL", "Steel"), - ("WOOD", "Wood"), - ("HYBRID", "Hybrid"), - ("OTHER", "Other"), - ], - default="STEEL", - max_length=20, - ), - ), - ( - "roller_coaster_type", - models.CharField( - blank=True, - choices=[ - ("SITDOWN", "Sit-Down"), - ("INVERTED", "Inverted"), - ("FLYING", "Flying"), - ("STANDUP", "Stand-Up"), - ("WING", "Wing"), - ("SUSPENDED", "Suspended"), - ("BOBSLED", "Bobsled"), - ("PIPELINE", "Pipeline"), - ("MOTORBIKE", "Motorbike"), - ("FLOORLESS", "Floorless"), - ("DIVE", "Dive"), - ("FAMILY", "Family"), - ("WILD_MOUSE", "Wild Mouse"), - ("SPINNING", "Spinning"), - ("FOURTH_DIMENSION", "4th Dimension"), - ("OTHER", "Other"), - ], - default="SITDOWN", - help_text="The type/style of roller coaster (e.g. Sit-Down, Inverted, Flying)", - max_length=20, - ), - ), - ( - "max_drop_height_ft", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Maximum vertical drop height in feet", - max_digits=6, - null=True, - ), - ), - ( - "launch_type", - models.CharField( - choices=[ - ("CHAIN", "Chain Lift"), - ("CABLE", "Cable Launch"), - ("HYDRAULIC", "Hydraulic Launch"), - ("LSM", "Linear Synchronous Motor"), - ("LIM", "Linear Induction Motor"), - ("GRAVITY", "Gravity"), - ("OTHER", "Other"), - ], - default="CHAIN", - max_length=20, - ), - ), - ("train_style", models.CharField(blank=True, max_length=255)), - ("trains_count", models.PositiveIntegerField(blank=True, null=True)), - ("cars_per_train", models.PositiveIntegerField(blank=True, null=True)), - ("seats_per_car", models.PositiveIntegerField(blank=True, null=True)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "ride", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="rides.ride", - ), - ), - ], - options={ - "verbose_name": "historical Roller Coaster Statistics", - "verbose_name_plural": "historical Roller Coaster Statistics", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="RollerCoasterStats", - fields=[ - ("id", models.BigAutoField(primary_key=True, serialize=False)), - ( - "height_ft", - 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 - ), - ), - ( - "speed_mph", - models.DecimalField( - blank=True, decimal_places=2, max_digits=5, null=True - ), - ), - ("inversions", models.PositiveIntegerField(default=0)), - ( - "ride_time_seconds", - models.PositiveIntegerField(blank=True, null=True), - ), - ("track_type", models.CharField(blank=True, max_length=255)), - ( - "track_material", - models.CharField( - blank=True, - choices=[ - ("STEEL", "Steel"), - ("WOOD", "Wood"), - ("HYBRID", "Hybrid"), - ("OTHER", "Other"), - ], - default="STEEL", - max_length=20, - ), - ), - ( - "roller_coaster_type", - models.CharField( - blank=True, - choices=[ - ("SITDOWN", "Sit-Down"), - ("INVERTED", "Inverted"), - ("FLYING", "Flying"), - ("STANDUP", "Stand-Up"), - ("WING", "Wing"), - ("SUSPENDED", "Suspended"), - ("BOBSLED", "Bobsled"), - ("PIPELINE", "Pipeline"), - ("MOTORBIKE", "Motorbike"), - ("FLOORLESS", "Floorless"), - ("DIVE", "Dive"), - ("FAMILY", "Family"), - ("WILD_MOUSE", "Wild Mouse"), - ("SPINNING", "Spinning"), - ("FOURTH_DIMENSION", "4th Dimension"), - ("OTHER", "Other"), - ], - default="SITDOWN", - help_text="The type/style of roller coaster (e.g. Sit-Down, Inverted, Flying)", - max_length=20, - ), - ), - ( - "max_drop_height_ft", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Maximum vertical drop height in feet", - max_digits=6, - null=True, - ), - ), - ( - "launch_type", - models.CharField( - choices=[ - ("CHAIN", "Chain Lift"), - ("CABLE", "Cable Launch"), - ("HYDRAULIC", "Hydraulic Launch"), - ("LSM", "Linear Synchronous Motor"), - ("LIM", "Linear Induction Motor"), - ("GRAVITY", "Gravity"), - ("OTHER", "Other"), - ], - default="CHAIN", - max_length=20, - ), - ), - ("train_style", models.CharField(blank=True, max_length=255)), - ("trains_count", models.PositiveIntegerField(blank=True, null=True)), - ("cars_per_train", models.PositiveIntegerField(blank=True, null=True)), - ("seats_per_car", models.PositiveIntegerField(blank=True, null=True)), - ( - "ride", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="coaster_stats", - to="rides.ride", - ), - ), - ], - options={ - "verbose_name": "Roller Coaster Statistics", - "verbose_name_plural": "Roller Coaster Statistics", - }, - ), - ] diff --git a/rides/migrations/0002_alter_historicalride_designer_alter_ride_designer.py b/rides/migrations/0002_alter_historicalride_designer_alter_ride_designer.py deleted file mode 100644 index d32a66a9..00000000 --- a/rides/migrations/0002_alter_historicalride_designer_alter_ride_designer.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-12 20:23 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0002_add_designer_model"), - ("rides", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="historicalride", - name="designer", - field=models.ForeignKey( - blank=True, - db_constraint=False, - help_text="The designer/engineering firm responsible for the ride", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="companies.designer", - ), - ), - migrations.AlterField( - model_name="ride", - name="designer", - field=models.ForeignKey( - blank=True, - help_text="The designer/engineering firm responsible for the ride", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="companies.designer", - ), - ), - ] diff --git a/rides/migrations/0003_remove_historicalride_accessibility_options_and_more.py b/rides/migrations/0003_remove_historicalride_accessibility_options_and_more.py deleted file mode 100644 index cc6878f9..00000000 --- a/rides/migrations/0003_remove_historicalride_accessibility_options_and_more.py +++ /dev/null @@ -1,160 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-12 21:40 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0002_add_designer_model"), - ("rides", "0002_alter_historicalride_designer_alter_ride_designer"), - ] - - operations = [ - migrations.RemoveField( - model_name="historicalride", - name="accessibility_options", - ), - migrations.RemoveField( - model_name="ride", - name="accessibility_options", - ), - migrations.AlterField( - model_name="historicalride", - name="category", - field=models.CharField( - blank=True, - choices=[ - ("", "Select ride type... *"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - migrations.AlterField( - model_name="historicalride", - name="designer", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="companies.designer", - ), - ), - migrations.AlterField( - model_name="historicalrollercoasterstats", - name="max_drop_height_ft", - field=models.DecimalField( - blank=True, decimal_places=2, max_digits=6, null=True - ), - ), - migrations.AlterField( - model_name="historicalrollercoasterstats", - name="roller_coaster_type", - field=models.CharField( - blank=True, - choices=[ - ("SITDOWN", "Sit-Down"), - ("INVERTED", "Inverted"), - ("FLYING", "Flying"), - ("STANDUP", "Stand-Up"), - ("WING", "Wing"), - ("SUSPENDED", "Suspended"), - ("BOBSLED", "Bobsled"), - ("PIPELINE", "Pipeline"), - ("MOTORBIKE", "Motorbike"), - ("FLOORLESS", "Floorless"), - ("DIVE", "Dive"), - ("FAMILY", "Family"), - ("WILD_MOUSE", "Wild Mouse"), - ("SPINNING", "Spinning"), - ("FOURTH_DIMENSION", "4th Dimension"), - ("OTHER", "Other"), - ], - default="SITDOWN", - max_length=20, - ), - ), - migrations.AlterField( - model_name="ride", - name="category", - field=models.CharField( - blank=True, - choices=[ - ("", "Select ride type... *"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - migrations.AlterField( - model_name="ride", - name="designer", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="companies.designer", - ), - ), - migrations.AlterField( - model_name="ride", - name="manufacturer", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="companies.manufacturer", - ), - ), - migrations.AlterField( - model_name="rollercoasterstats", - name="max_drop_height_ft", - field=models.DecimalField( - blank=True, decimal_places=2, max_digits=6, null=True - ), - ), - migrations.AlterField( - model_name="rollercoasterstats", - name="roller_coaster_type", - field=models.CharField( - blank=True, - choices=[ - ("SITDOWN", "Sit-Down"), - ("INVERTED", "Inverted"), - ("FLYING", "Flying"), - ("STANDUP", "Stand-Up"), - ("WING", "Wing"), - ("SUSPENDED", "Suspended"), - ("BOBSLED", "Bobsled"), - ("PIPELINE", "Pipeline"), - ("MOTORBIKE", "Motorbike"), - ("FLOORLESS", "Floorless"), - ("DIVE", "Dive"), - ("FAMILY", "Family"), - ("WILD_MOUSE", "Wild Mouse"), - ("SPINNING", "Spinning"), - ("FOURTH_DIMENSION", "4th Dimension"), - ("OTHER", "Other"), - ], - default="SITDOWN", - max_length=20, - ), - ), - ] diff --git a/rides/migrations/0004_alter_historicalride_category_alter_ride_category.py b/rides/migrations/0004_alter_historicalride_category_alter_ride_category.py deleted file mode 100644 index 4e1ff81a..00000000 --- a/rides/migrations/0004_alter_historicalride_category_alter_ride_category.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-12 21:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("rides", "0003_remove_historicalride_accessibility_options_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="historicalride", - name="category", - field=models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - migrations.AlterField( - model_name="ride", - name="category", - field=models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - ] diff --git a/rides/migrations/0005_alter_rollercoasterstats_id_and_more.py b/rides/migrations/0005_alter_rollercoasterstats_id_and_more.py deleted file mode 100644 index 84c89a9d..00000000 --- a/rides/migrations/0005_alter_rollercoasterstats_id_and_more.py +++ /dev/null @@ -1,259 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-12 22:27 - -import django.db.models.deletion -import simple_history.models -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0002_add_designer_model"), - ("rides", "0004_alter_historicalride_category_alter_ride_category"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name="rollercoasterstats", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="rollercoasterstats", - name="launch_type", - field=models.CharField( - choices=[ - ("CHAIN", "Chain Lift"), - ("LSM", "LSM Launch"), - ("HYDRAULIC", "Hydraulic Launch"), - ("GRAVITY", "Gravity"), - ("OTHER", "Other"), - ], - default="CHAIN", - max_length=20, - ), - ), - migrations.AlterField( - model_name="rollercoasterstats", - name="roller_coaster_type", - field=models.CharField( - blank=True, - choices=[ - ("SITDOWN", "Sit Down"), - ("INVERTED", "Inverted"), - ("FLYING", "Flying"), - ("STANDUP", "Stand Up"), - ("WING", "Wing"), - ("DIVE", "Dive"), - ("FAMILY", "Family"), - ("WILD_MOUSE", "Wild Mouse"), - ("SPINNING", "Spinning"), - ("FOURTH_DIMENSION", "4th Dimension"), - ("OTHER", "Other"), - ], - default="SITDOWN", - max_length=20, - ), - ), - migrations.AlterField( - model_name="rollercoasterstats", - name="track_material", - field=models.CharField( - blank=True, - choices=[("STEEL", "Steel"), ("WOOD", "Wood"), ("HYBRID", "Hybrid")], - default="STEEL", - max_length=20, - ), - ), - migrations.CreateModel( - name="HistoricalRideModel", - fields=[ - ("id", models.BigIntegerField(blank=True, db_index=True)), - ("name", models.CharField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "typical_height_ft", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Typical height of this model in feet", - max_digits=6, - null=True, - ), - ), - ( - "typical_speed_mph", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Typical speed of this model in mph", - max_digits=5, - null=True, - ), - ), - ( - "typical_capacity_per_hour", - models.PositiveIntegerField( - blank=True, - help_text="Typical hourly capacity of this model", - null=True, - ), - ), - ( - "category", - models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - ("created_at", models.DateTimeField(blank=True, editable=False)), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "manufacturer", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="companies.manufacturer", - ), - ), - ], - options={ - "verbose_name": "historical ride model", - "verbose_name_plural": "historical ride models", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="RideModel", - fields=[ - ("id", models.BigAutoField(primary_key=True, serialize=False)), - ("name", models.CharField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "typical_height_ft", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Typical height of this model in feet", - max_digits=6, - null=True, - ), - ), - ( - "typical_speed_mph", - models.DecimalField( - blank=True, - decimal_places=2, - help_text="Typical speed of this model in mph", - max_digits=5, - null=True, - ), - ), - ( - "typical_capacity_per_hour", - models.PositiveIntegerField( - blank=True, - help_text="Typical hourly capacity of this model", - null=True, - ), - ), - ( - "category", - models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ( - "manufacturer", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="ride_models", - to="companies.manufacturer", - ), - ), - ], - options={ - "ordering": ["manufacturer", "name"], - "unique_together": {("manufacturer", "name")}, - }, - ), - migrations.AddField( - model_name="historicalride", - name="ride_model", - field=models.ForeignKey( - blank=True, - db_constraint=False, - help_text="The specific model/type of this ride", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="rides.ridemodel", - ), - ), - migrations.AddField( - model_name="ride", - name="ride_model", - field=models.ForeignKey( - blank=True, - help_text="The specific model/type of this ride", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="rides.ridemodel", - ), - ), - migrations.DeleteModel( - name="HistoricalRollerCoasterStats", - ), - ] diff --git a/rides/migrations/0006_remove_historicalridemodel_typical_capacity_per_hour_and_more.py b/rides/migrations/0006_remove_historicalridemodel_typical_capacity_per_hour_and_more.py deleted file mode 100644 index aa8e716c..00000000 --- a/rides/migrations/0006_remove_historicalridemodel_typical_capacity_per_hour_and_more.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 00:20 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("rides", "0005_alter_rollercoasterstats_id_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="historicalridemodel", - name="typical_capacity_per_hour", - ), - migrations.RemoveField( - model_name="historicalridemodel", - name="typical_height_ft", - ), - migrations.RemoveField( - model_name="historicalridemodel", - name="typical_speed_mph", - ), - migrations.RemoveField( - model_name="ridemodel", - name="typical_capacity_per_hour", - ), - migrations.RemoveField( - model_name="ridemodel", - name="typical_height_ft", - ), - migrations.RemoveField( - model_name="ridemodel", - name="typical_speed_mph", - ), - ] diff --git a/rides/migrations/0007_alter_ridemodel_manufacturer.py b/rides/migrations/0007_alter_ridemodel_manufacturer.py deleted file mode 100644 index 2ac55f46..00000000 --- a/rides/migrations/0007_alter_ridemodel_manufacturer.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 02:14 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0002_add_designer_model"), - ("rides", "0006_remove_historicalridemodel_typical_capacity_per_hour_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="ridemodel", - name="manufacturer", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="ride_models", - to="companies.manufacturer", - ), - ), - ] diff --git a/rides/migrations/0008_historicalride_post_closing_status_and_more.py b/rides/migrations/0008_historicalride_post_closing_status_and_more.py deleted file mode 100644 index 86856ca8..00000000 --- a/rides/migrations/0008_historicalride_post_closing_status_and_more.py +++ /dev/null @@ -1,75 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 04:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("rides", "0007_alter_ridemodel_manufacturer"), - ] - - operations = [ - migrations.AddField( - model_name="historicalride", - name="post_closing_status", - field=models.CharField( - blank=True, - choices=[ - ("SBNO", "Standing But Not Operating"), - ("CLOSED_PERM", "Permanently Closed"), - ], - help_text="Status to change to after closing date", - max_length=20, - null=True, - ), - ), - migrations.AddField( - model_name="ride", - name="post_closing_status", - field=models.CharField( - blank=True, - choices=[ - ("SBNO", "Standing But Not Operating"), - ("CLOSED_PERM", "Permanently Closed"), - ], - help_text="Status to change to after closing date", - max_length=20, - null=True, - ), - ), - migrations.AlterField( - model_name="historicalride", - name="status", - field=models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("SBNO", "Standing But Not Operating"), - ("CLOSING", "Closing"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - migrations.AlterField( - model_name="ride", - name="status", - field=models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("SBNO", "Standing But Not Operating"), - ("CLOSING", "Closing"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ] diff --git a/rides/migrations/0009_remove_historicalride_model_name_and_more.py b/rides/migrations/0009_remove_historicalride_model_name_and_more.py deleted file mode 100644 index da2d314f..00000000 --- a/rides/migrations/0009_remove_historicalride_model_name_and_more.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 04:44 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("rides", "0008_historicalride_post_closing_status_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="historicalride", - name="model_name", - ), - migrations.RemoveField( - model_name="ride", - name="model_name", - ), - ] diff --git a/rides/migrations/0010_rideevent_ridemodelevent_and_more.py b/rides/migrations/0010_rideevent_ridemodelevent_and_more.py deleted file mode 100644 index 21aca265..00000000 --- a/rides/migrations/0010_rideevent_ridemodelevent_and_more.py +++ /dev/null @@ -1,364 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:31 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0003_companyevent_manufacturerevent"), - ( - "designers", - "0002_designerevent_remove_historicaldesigner_history_user_and_more", - ), - ("parks", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - ("rides", "0009_remove_historicalride_model_name_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="RideEvent", - fields=[ - ("pgh_id", models.AutoField(primary_key=True, serialize=False)), - ("pgh_created_at", models.DateTimeField(auto_now_add=True)), - ("pgh_label", models.TextField(help_text="The event label.")), - ("id", models.BigIntegerField()), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(db_index=False, max_length=255)), - ("description", models.TextField(blank=True)), - ( - "category", - models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - ( - "status", - models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("SBNO", "Standing But Not Operating"), - ("CLOSING", "Closing"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ( - "post_closing_status", - models.CharField( - blank=True, - choices=[ - ("SBNO", "Standing But Not Operating"), - ("CLOSED_PERM", "Permanently Closed"), - ], - help_text="Status to change to after closing date", - max_length=20, - null=True, - ), - ), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("status_since", models.DateField(blank=True, null=True)), - ("min_height_in", models.PositiveIntegerField(blank=True, null=True)), - ("max_height_in", models.PositiveIntegerField(blank=True, null=True)), - ( - "capacity_per_hour", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "ride_duration_seconds", - models.PositiveIntegerField(blank=True, null=True), - ), - ( - "average_rating", - models.DecimalField( - blank=True, decimal_places=2, max_digits=3, null=True - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="RideModelEvent", - fields=[ - ("pgh_id", models.AutoField(primary_key=True, serialize=False)), - ("pgh_created_at", models.DateTimeField(auto_now_add=True)), - ("pgh_label", models.TextField(help_text="The event label.")), - ("id", models.BigIntegerField()), - ("name", models.CharField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "category", - models.CharField( - blank=True, - choices=[ - ("", "Select ride type"), - ("RC", "Roller Coaster"), - ("DR", "Dark Ride"), - ("FR", "Flat Ride"), - ("WR", "Water Ride"), - ("TR", "Transport"), - ("OT", "Other"), - ], - default="", - max_length=2, - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.RemoveField( - model_name="historicalride", - name="designer", - ), - migrations.RemoveField( - model_name="historicalride", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalride", - name="manufacturer", - ), - migrations.RemoveField( - model_name="historicalride", - name="park", - ), - migrations.RemoveField( - model_name="historicalride", - name="park_area", - ), - migrations.RemoveField( - model_name="historicalride", - name="ride_model", - ), - migrations.RemoveField( - model_name="historicalridemodel", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalridemodel", - name="manufacturer", - ), - migrations.AlterField( - model_name="ride", - name="designer", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="rides", - to="designers.designer", - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="ride", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "rides_rideevent" ("average_rating", "capacity_per_hour", "category", "closing_date", "created_at", "description", "designer_id", "id", "manufacturer_id", "max_height_in", "min_height_in", "name", "opening_date", "park_area_id", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "post_closing_status", "ride_duration_seconds", "ride_model_id", "slug", "status", "status_since", "updated_at") VALUES (NEW."average_rating", NEW."capacity_per_hour", NEW."category", NEW."closing_date", NEW."created_at", NEW."description", NEW."designer_id", NEW."id", NEW."manufacturer_id", NEW."max_height_in", NEW."min_height_in", NEW."name", NEW."opening_date", NEW."park_area_id", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."post_closing_status", NEW."ride_duration_seconds", NEW."ride_model_id", NEW."slug", NEW."status", NEW."status_since", NEW."updated_at"); RETURN NULL;', - hash="[AWS-SECRET-REMOVED]", - operation="INSERT", - pgid="pgtrigger_insert_insert_52074", - table="rides_ride", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="ride", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "rides_rideevent" ("average_rating", "capacity_per_hour", "category", "closing_date", "created_at", "description", "designer_id", "id", "manufacturer_id", "max_height_in", "min_height_in", "name", "opening_date", "park_area_id", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "post_closing_status", "ride_duration_seconds", "ride_model_id", "slug", "status", "status_since", "updated_at") VALUES (NEW."average_rating", NEW."capacity_per_hour", NEW."category", NEW."closing_date", NEW."created_at", NEW."description", NEW."designer_id", NEW."id", NEW."manufacturer_id", NEW."max_height_in", NEW."min_height_in", NEW."name", NEW."opening_date", NEW."park_area_id", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."post_closing_status", NEW."ride_duration_seconds", NEW."ride_model_id", NEW."slug", NEW."status", NEW."status_since", NEW."updated_at"); RETURN NULL;', - hash="[AWS-SECRET-REMOVED]", - operation="UPDATE", - pgid="pgtrigger_update_update_4917a", - table="rides_ride", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="ridemodel", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "rides_ridemodelevent" ("category", "created_at", "description", "id", "manufacturer_id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", NEW."manufacturer_id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at"); RETURN NULL;', - hash="[AWS-SECRET-REMOVED]", - operation="INSERT", - pgid="pgtrigger_insert_insert_0aaee", - table="rides_ridemodel", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="ridemodel", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "rides_ridemodelevent" ("category", "created_at", "description", "id", "manufacturer_id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", NEW."manufacturer_id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at"); RETURN NULL;', - hash="[AWS-SECRET-REMOVED]", - operation="UPDATE", - pgid="pgtrigger_update_update_0ca1a", - table="rides_ridemodel", - when="AFTER", - ), - ), - ), - migrations.AddField( - model_name="rideevent", - name="designer", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="designers.designer", - ), - ), - migrations.AddField( - model_name="rideevent", - name="manufacturer", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="companies.manufacturer", - ), - ), - migrations.AddField( - model_name="rideevent", - name="park", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="parks.park", - ), - ), - migrations.AddField( - model_name="rideevent", - name="park_area", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="parks.parkarea", - ), - ), - migrations.AddField( - model_name="rideevent", - name="pgh_context", - field=models.ForeignKey( - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="pghistory.context", - ), - ), - migrations.AddField( - model_name="rideevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="rides.ride", - ), - ), - migrations.AddField( - model_name="rideevent", - name="ride_model", - field=models.ForeignKey( - blank=True, - db_constraint=False, - help_text="The specific model/type of this ride", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="rides.ridemodel", - ), - ), - migrations.AddField( - model_name="ridemodelevent", - name="manufacturer", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="companies.manufacturer", - ), - ), - migrations.AddField( - model_name="ridemodelevent", - name="pgh_context", - field=models.ForeignKey( - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="pghistory.context", - ), - ), - migrations.AddField( - model_name="ridemodelevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="rides.ridemodel", - ), - ), - migrations.DeleteModel( - name="HistoricalRide", - ), - migrations.DeleteModel( - name="HistoricalRideModel", - ), - ] diff --git a/tests/test_runner.py b/tests/test_runner.py index e2d56a71..139b7077 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -4,7 +4,7 @@ import sys import django from django.conf import settings from django.test.runner import DiscoverRunner -import coverage +import coverage # type: ignore import unittest def setup_django(): diff --git a/thrillwiki/settings.py b/thrillwiki/settings.py index f44afe9e..863970e3 100644 --- a/thrillwiki/settings.py +++ b/thrillwiki/settings.py @@ -68,7 +68,7 @@ MIDDLEWARE = [ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - "core.middleware.setup_pghistory_context", # Add history context tracking + "core.middleware.PgHistoryContextMiddleware", # Add history context tracking "allauth.account.middleware.AccountMiddleware", "django.middleware.cache.FetchFromCacheMiddleware", "simple_history.middleware.HistoryRequestMiddleware",