Add ReviewEvent model and ReviewSubmissionService for review management

- Created a new ReviewEvent model to track review events with fields for content, rating, moderation status, and timestamps.
- Added ForeignKey relationships to connect ReviewEvent with ContentSubmission, User, and Review.
- Implemented ReviewSubmissionService to handle review submissions, including creation, updates, and moderation workflows.
- Introduced atomic transactions to ensure data integrity during review submissions and updates.
- Added logging for review submission and moderation actions for better traceability.
- Implemented validation to prevent duplicate reviews and ensure only the review owner can update their review.
This commit is contained in:
pacnpal
2025-11-08 16:49:58 -05:00
parent 618310a87b
commit 9122320e7e
18 changed files with 3170 additions and 171 deletions

View File

@@ -0,0 +1,936 @@
# Generated by Django 4.2.8 on 2025-11-08 21:37
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
import pgtrigger.compiler
import pgtrigger.migrations
import uuid
class Migration(migrations.Migration):
dependencies = [
("core", "0001_initial"),
("pghistory", "0006_delete_aggregateevent"),
("entities", "0003_add_search_vector_gin_indexes"),
]
operations = [
migrations.CreateModel(
name="CompanyEvent",
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.")),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, serialize=False
),
),
(
"name",
models.CharField(help_text="Official company name", max_length=255),
),
(
"slug",
models.SlugField(
db_index=False,
help_text="URL-friendly identifier",
max_length=255,
),
),
(
"description",
models.TextField(
blank=True, help_text="Company description and history"
),
),
(
"company_types",
models.JSONField(
default=list,
help_text="List of company types (manufacturer, operator, etc.)",
),
),
(
"founded_date",
models.DateField(
blank=True, help_text="Company founding date", null=True
),
),
(
"founded_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of founded date",
max_length=20,
),
),
(
"closed_date",
models.DateField(
blank=True,
help_text="Company closure date (if applicable)",
null=True,
),
),
(
"closed_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of closed date",
max_length=20,
),
),
(
"website",
models.URLField(blank=True, help_text="Official company website"),
),
(
"logo_image_id",
models.CharField(
blank=True,
help_text="CloudFlare image ID for company logo",
max_length=255,
),
),
(
"logo_image_url",
models.URLField(
blank=True, help_text="CloudFlare image URL for company logo"
),
),
(
"park_count",
models.IntegerField(
default=0, help_text="Number of parks operated (for operators)"
),
),
(
"ride_count",
models.IntegerField(
default=0,
help_text="Number of rides manufactured (for manufacturers)",
),
),
],
options={
"abstract": False,
},
),
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.")),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, serialize=False
),
),
(
"name",
models.CharField(help_text="Official park name", max_length=255),
),
(
"slug",
models.SlugField(
db_index=False,
help_text="URL-friendly identifier",
max_length=255,
),
),
(
"description",
models.TextField(
blank=True, help_text="Park description and history"
),
),
(
"park_type",
models.CharField(
choices=[
("theme_park", "Theme Park"),
("amusement_park", "Amusement Park"),
("water_park", "Water Park"),
(
"family_entertainment_center",
"Family Entertainment Center",
),
("traveling_park", "Traveling Park"),
("zoo", "Zoo"),
("aquarium", "Aquarium"),
],
help_text="Type of park",
max_length=50,
),
),
(
"status",
models.CharField(
choices=[
("operating", "Operating"),
("closed", "Closed"),
("sbno", "Standing But Not Operating"),
("under_construction", "Under Construction"),
("planned", "Planned"),
],
default="operating",
help_text="Current operational status",
max_length=50,
),
),
(
"opening_date",
models.DateField(
blank=True, help_text="Park opening date", null=True
),
),
(
"opening_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of opening date",
max_length=20,
),
),
(
"closing_date",
models.DateField(
blank=True, help_text="Park closing date (if closed)", null=True
),
),
(
"closing_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of closing date",
max_length=20,
),
),
(
"latitude",
models.DecimalField(
blank=True,
decimal_places=7,
help_text="Latitude coordinate. Primary in local dev, use location_point in production.",
max_digits=10,
null=True,
),
),
(
"longitude",
models.DecimalField(
blank=True,
decimal_places=7,
help_text="Longitude coordinate. Primary in local dev, use location_point in production.",
max_digits=10,
null=True,
),
),
(
"website",
models.URLField(blank=True, help_text="Official park website"),
),
(
"banner_image_id",
models.CharField(
blank=True,
help_text="CloudFlare image ID for park banner",
max_length=255,
),
),
(
"banner_image_url",
models.URLField(
blank=True, help_text="CloudFlare image URL for park banner"
),
),
(
"logo_image_id",
models.CharField(
blank=True,
help_text="CloudFlare image ID for park logo",
max_length=255,
),
),
(
"logo_image_url",
models.URLField(
blank=True, help_text="CloudFlare image URL for park logo"
),
),
(
"ride_count",
models.IntegerField(default=0, help_text="Total number of rides"),
),
(
"coaster_count",
models.IntegerField(
default=0, help_text="Number of roller coasters"
),
),
(
"custom_fields",
models.JSONField(
blank=True,
default=dict,
help_text="Additional park-specific data",
),
),
],
options={
"abstract": False,
},
),
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.")),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, serialize=False
),
),
("name", models.CharField(help_text="Ride name", max_length=255)),
(
"slug",
models.SlugField(
db_index=False,
help_text="URL-friendly identifier",
max_length=255,
),
),
(
"description",
models.TextField(
blank=True, help_text="Ride description and history"
),
),
(
"ride_category",
models.CharField(
choices=[
("roller_coaster", "Roller Coaster"),
("flat_ride", "Flat Ride"),
("water_ride", "Water Ride"),
("dark_ride", "Dark Ride"),
("transport_ride", "Transport Ride"),
("other", "Other"),
],
help_text="Broad ride category",
max_length=50,
),
),
(
"ride_type",
models.CharField(
blank=True,
help_text="Specific ride type (e.g., 'Inverted Coaster', 'Drop Tower')",
max_length=100,
),
),
(
"is_coaster",
models.BooleanField(
default=False, help_text="Is this ride a roller coaster?"
),
),
(
"status",
models.CharField(
choices=[
("operating", "Operating"),
("closed", "Closed"),
("sbno", "Standing But Not Operating"),
("relocated", "Relocated"),
("under_construction", "Under Construction"),
("planned", "Planned"),
],
default="operating",
help_text="Current operational status",
max_length=50,
),
),
(
"opening_date",
models.DateField(
blank=True, help_text="Ride opening date", null=True
),
),
(
"opening_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of opening date",
max_length=20,
),
),
(
"closing_date",
models.DateField(
blank=True, help_text="Ride closing date (if closed)", null=True
),
),
(
"closing_date_precision",
models.CharField(
choices=[("year", "Year"), ("month", "Month"), ("day", "Day")],
default="day",
help_text="Precision of closing date",
max_length=20,
),
),
(
"height",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Height in feet",
max_digits=6,
null=True,
),
),
(
"speed",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Top speed in mph",
max_digits=6,
null=True,
),
),
(
"length",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Track/ride length in feet",
max_digits=8,
null=True,
),
),
(
"duration",
models.IntegerField(
blank=True, help_text="Ride duration in seconds", null=True
),
),
(
"inversions",
models.IntegerField(
blank=True,
help_text="Number of inversions (for coasters)",
null=True,
),
),
(
"capacity",
models.IntegerField(
blank=True,
help_text="Hourly capacity (riders per hour)",
null=True,
),
),
(
"image_id",
models.CharField(
blank=True,
help_text="CloudFlare image ID for main photo",
max_length=255,
),
),
(
"image_url",
models.URLField(
blank=True, help_text="CloudFlare image URL for main photo"
),
),
(
"custom_fields",
models.JSONField(
blank=True,
default=dict,
help_text="Additional ride-specific data",
),
),
],
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.")),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, serialize=False
),
),
(
"name",
models.CharField(
help_text="Model name (e.g., 'Inverted Coaster', 'Boomerang')",
max_length=255,
),
),
(
"slug",
models.SlugField(
db_index=False,
help_text="URL-friendly identifier",
max_length=255,
),
),
(
"description",
models.TextField(
blank=True, help_text="Model description and technical details"
),
),
(
"model_type",
models.CharField(
choices=[
("coaster_model", "Roller Coaster Model"),
("flat_ride_model", "Flat Ride Model"),
("water_ride_model", "Water Ride Model"),
("dark_ride_model", "Dark Ride Model"),
("transport_ride_model", "Transport Ride Model"),
],
help_text="Type of ride model",
max_length=50,
),
),
(
"typical_height",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Typical height in feet",
max_digits=6,
null=True,
),
),
(
"typical_speed",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Typical speed in mph",
max_digits=6,
null=True,
),
),
(
"typical_capacity",
models.IntegerField(
blank=True, help_text="Typical hourly capacity", null=True
),
),
(
"image_id",
models.CharField(
blank=True, help_text="CloudFlare image ID", max_length=255
),
),
(
"image_url",
models.URLField(blank=True, help_text="CloudFlare image URL"),
),
(
"installation_count",
models.IntegerField(
default=0, help_text="Number of installations worldwide"
),
),
],
options={
"abstract": False,
},
),
pgtrigger.migrations.AddTrigger(
model_name="company",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "entities_companyevent" ("closed_date", "closed_date_precision", "company_types", "created", "description", "founded_date", "founded_date_precision", "id", "location_id", "logo_image_id", "logo_image_url", "modified", "name", "park_count", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "slug", "website") VALUES (NEW."closed_date", NEW."closed_date_precision", NEW."company_types", NEW."created", NEW."description", NEW."founded_date", NEW."founded_date_precision", NEW."id", NEW."location_id", NEW."logo_image_id", NEW."logo_image_url", NEW."modified", NEW."name", NEW."park_count", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."slug", NEW."website"); RETURN NULL;',
hash="891243f1479adc9ae67c894ec6824b89b7997086",
operation="INSERT",
pgid="pgtrigger_insert_insert_ed498",
table="entities_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 "entities_companyevent" ("closed_date", "closed_date_precision", "company_types", "created", "description", "founded_date", "founded_date_precision", "id", "location_id", "logo_image_id", "logo_image_url", "modified", "name", "park_count", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "slug", "website") VALUES (NEW."closed_date", NEW."closed_date_precision", NEW."company_types", NEW."created", NEW."description", NEW."founded_date", NEW."founded_date_precision", NEW."id", NEW."location_id", NEW."logo_image_id", NEW."logo_image_url", NEW."modified", NEW."name", NEW."park_count", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."slug", NEW."website"); RETURN NULL;',
hash="5d0f3d8dbb199afd7474de393b075b8e72c481fd",
operation="UPDATE",
pgid="pgtrigger_update_update_2d89e",
table="entities_company",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="park",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "entities_parkevent" ("banner_image_id", "banner_image_url", "closing_date", "closing_date_precision", "coaster_count", "created", "custom_fields", "description", "id", "latitude", "location_id", "logo_image_id", "logo_image_url", "longitude", "modified", "name", "opening_date", "opening_date_precision", "operator_id", "park_type", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "slug", "status", "website") VALUES (NEW."banner_image_id", NEW."banner_image_url", NEW."closing_date", NEW."closing_date_precision", NEW."coaster_count", NEW."created", NEW."custom_fields", NEW."description", NEW."id", NEW."latitude", NEW."location_id", NEW."logo_image_id", NEW."logo_image_url", NEW."longitude", NEW."modified", NEW."name", NEW."opening_date", NEW."opening_date_precision", NEW."operator_id", NEW."park_type", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."slug", NEW."status", NEW."website"); RETURN NULL;',
hash="e03ce2a0516ff75f1703a6ccf069ce931f3123bc",
operation="INSERT",
pgid="pgtrigger_insert_insert_a5515",
table="entities_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 "entities_parkevent" ("banner_image_id", "banner_image_url", "closing_date", "closing_date_precision", "coaster_count", "created", "custom_fields", "description", "id", "latitude", "location_id", "logo_image_id", "logo_image_url", "longitude", "modified", "name", "opening_date", "opening_date_precision", "operator_id", "park_type", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "slug", "status", "website") VALUES (NEW."banner_image_id", NEW."banner_image_url", NEW."closing_date", NEW."closing_date_precision", NEW."coaster_count", NEW."created", NEW."custom_fields", NEW."description", NEW."id", NEW."latitude", NEW."location_id", NEW."logo_image_id", NEW."logo_image_url", NEW."longitude", NEW."modified", NEW."name", NEW."opening_date", NEW."opening_date_precision", NEW."operator_id", NEW."park_type", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."slug", NEW."status", NEW."website"); RETURN NULL;',
hash="0e01b4eac8ef56aeb039c870c7ac194d2615012e",
operation="UPDATE",
pgid="pgtrigger_update_update_b436a",
table="entities_park",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ride",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "entities_rideevent" ("capacity", "closing_date", "closing_date_precision", "created", "custom_fields", "description", "duration", "height", "id", "image_id", "image_url", "inversions", "is_coaster", "length", "manufacturer_id", "model_id", "modified", "name", "opening_date", "opening_date_precision", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_category", "ride_type", "slug", "speed", "status") VALUES (NEW."capacity", NEW."closing_date", NEW."closing_date_precision", NEW."created", NEW."custom_fields", NEW."description", NEW."duration", NEW."height", NEW."id", NEW."image_id", NEW."image_url", NEW."inversions", NEW."is_coaster", NEW."length", NEW."manufacturer_id", NEW."model_id", NEW."modified", NEW."name", NEW."opening_date", NEW."opening_date_precision", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_category", NEW."ride_type", NEW."slug", NEW."speed", NEW."status"); RETURN NULL;',
hash="02f95397d881bd95627424df1a144956d5f15f8d",
operation="INSERT",
pgid="pgtrigger_insert_insert_23173",
table="entities_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 "entities_rideevent" ("capacity", "closing_date", "closing_date_precision", "created", "custom_fields", "description", "duration", "height", "id", "image_id", "image_url", "inversions", "is_coaster", "length", "manufacturer_id", "model_id", "modified", "name", "opening_date", "opening_date_precision", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_category", "ride_type", "slug", "speed", "status") VALUES (NEW."capacity", NEW."closing_date", NEW."closing_date_precision", NEW."created", NEW."custom_fields", NEW."description", NEW."duration", NEW."height", NEW."id", NEW."image_id", NEW."image_url", NEW."inversions", NEW."is_coaster", NEW."length", NEW."manufacturer_id", NEW."model_id", NEW."modified", NEW."name", NEW."opening_date", NEW."opening_date_precision", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_category", NEW."ride_type", NEW."slug", NEW."speed", NEW."status"); RETURN NULL;',
hash="9377ca0c44ec8e548254d371a95e9ff7a6eb8684",
operation="UPDATE",
pgid="pgtrigger_update_update_c2972",
table="entities_ride",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ridemodel",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "entities_ridemodelevent" ("created", "description", "id", "image_id", "image_url", "installation_count", "manufacturer_id", "model_type", "modified", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "typical_capacity", "typical_height", "typical_speed") VALUES (NEW."created", NEW."description", NEW."id", NEW."image_id", NEW."image_url", NEW."installation_count", NEW."manufacturer_id", NEW."model_type", NEW."modified", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."typical_capacity", NEW."typical_height", NEW."typical_speed"); RETURN NULL;',
hash="580a9d8a429d5140bc6bf553d6e0f9c06b7a7dec",
operation="INSERT",
pgid="pgtrigger_insert_insert_04de6",
table="entities_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 "entities_ridemodelevent" ("created", "description", "id", "image_id", "image_url", "installation_count", "manufacturer_id", "model_type", "modified", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "typical_capacity", "typical_height", "typical_speed") VALUES (NEW."created", NEW."description", NEW."id", NEW."image_id", NEW."image_url", NEW."installation_count", NEW."manufacturer_id", NEW."model_type", NEW."modified", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."typical_capacity", NEW."typical_height", NEW."typical_speed"); RETURN NULL;',
hash="b7d6519a2c97e7b543494b67c4f25826439a02ef",
operation="UPDATE",
pgid="pgtrigger_update_update_a70fd",
table="entities_ridemodel",
when="AFTER",
),
),
),
migrations.AddField(
model_name="ridemodelevent",
name="manufacturer",
field=models.ForeignKey(
db_constraint=False,
help_text="Manufacturer of this ride model",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="entities.company",
),
),
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="+",
related_query_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",
related_query_name="+",
to="entities.ridemodel",
),
),
migrations.AddField(
model_name="rideevent",
name="manufacturer",
field=models.ForeignKey(
blank=True,
db_constraint=False,
help_text="Ride manufacturer",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="entities.company",
),
),
migrations.AddField(
model_name="rideevent",
name="model",
field=models.ForeignKey(
blank=True,
db_constraint=False,
help_text="Specific ride model",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="entities.ridemodel",
),
),
migrations.AddField(
model_name="rideevent",
name="park",
field=models.ForeignKey(
db_constraint=False,
help_text="Park where ride is located",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="entities.park",
),
),
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="+",
related_query_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",
related_query_name="+",
to="entities.ride",
),
),
migrations.AddField(
model_name="parkevent",
name="location",
field=models.ForeignKey(
blank=True,
db_constraint=False,
help_text="Park location",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="core.locality",
),
),
migrations.AddField(
model_name="parkevent",
name="operator",
field=models.ForeignKey(
blank=True,
db_constraint=False,
help_text="Current park operator",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="entities.company",
),
),
migrations.AddField(
model_name="parkevent",
name="pgh_context",
field=models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="pghistory.context",
),
),
migrations.AddField(
model_name="parkevent",
name="pgh_obj",
field=models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
related_query_name="+",
to="entities.park",
),
),
migrations.AddField(
model_name="companyevent",
name="location",
field=models.ForeignKey(
blank=True,
db_constraint=False,
help_text="Company headquarters location",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="core.locality",
),
),
migrations.AddField(
model_name="companyevent",
name="pgh_context",
field=models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="pghistory.context",
),
),
migrations.AddField(
model_name="companyevent",
name="pgh_obj",
field=models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
related_query_name="+",
to="entities.company",
),
),
]

View File

@@ -12,6 +12,7 @@ from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.utils.text import slugify
from django_lifecycle import hook, AFTER_CREATE, AFTER_UPDATE, BEFORE_SAVE
import pghistory
from apps.core.models import VersionedModel, BaseModel
@@ -27,6 +28,7 @@ if _using_postgis:
from django.contrib.postgres.search import SearchVectorField
@pghistory.track()
class Company(VersionedModel):
"""
Represents a company in the amusement industry.
@@ -194,6 +196,7 @@ class Company(VersionedModel):
return photos
@pghistory.track()
class RideModel(VersionedModel):
"""
Represents a specific ride model from a manufacturer.
@@ -328,6 +331,7 @@ class RideModel(VersionedModel):
return photos
@pghistory.track()
class Park(VersionedModel):
"""
Represents an amusement park, theme park, water park, or FEC.
@@ -638,6 +642,7 @@ if _using_postgis:
)
@pghistory.track()
class Ride(VersionedModel):
"""
Represents an individual ride or roller coaster.

View File

@@ -152,7 +152,6 @@ def generate_entity_report(entity_type, entity_id):
from apps.entities.models import Park, Ride, Company, RideModel
from apps.media.models import Photo
from apps.moderation.models import ContentSubmission
from apps.versioning.models import EntityVersion
try:
model_map = {
@@ -206,10 +205,8 @@ def generate_entity_report(entity_type, entity_id):
status='pending'
).count(),
},
'versions': EntityVersion.objects.filter(
content_type__model=entity_type.lower(),
object_id=entity_id
).count(),
# Version history now tracked via pghistory Event models
# Can query {ModelName}Event if needed (e.g., ParkEvent, RideEvent)
}
logger.info(f"Generated report for {entity_type} {entity_id}")