diff --git a/latest_migrations.manifest b/latest_migrations.manifest index adaab5c454cc1..db15377f8d9b8 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0015_add_verified_properties otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0353_add_5_minute_interval_to_batch_exports +posthog: 0354_adjust_personoverride_constraints sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/posthog/management/commands/test_migrations_are_safe.py b/posthog/management/commands/test_migrations_are_safe.py index 0dbf2534c8702..577397f9561fb 100644 --- a/posthog/management/commands/test_migrations_are_safe.py +++ b/posthog/management/commands/test_migrations_are_safe.py @@ -18,6 +18,9 @@ def _get_table(search_string: str, operation_sql: str) -> Optional[str]: def validate_migration_sql(sql) -> bool: + if "-- skip-test-migrations-are-safe" in sql: + return False + new_tables = _get_new_tables(sql) operations = sql.split("\n") tables_created_so_far: List[str] = [] diff --git a/posthog/migrations/0354_adjust_personoverride_constraints.py b/posthog/migrations/0354_adjust_personoverride_constraints.py new file mode 100644 index 0000000000000..a4c9f1ac94576 --- /dev/null +++ b/posthog/migrations/0354_adjust_personoverride_constraints.py @@ -0,0 +1,76 @@ +# Generated by Django 3.2.19 on 2023-10-11 21:32 + +from django.db import migrations, models +import django.db.models.deletion + +# `skip-test-migrations-are-safe` is used because our migraton checks get mad about changing +# constraints and columns, but we're stopping traffic to these tables and truncating them before +# doing the migration, so it's safe. +TRUNCATE_PERSONOVERRIDE_SQL = ( + "TRUNCATE posthog_personoverride, posthog_personoverridemapping -- skip-test-migrations-are-safe" +) + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0353_add_5_minute_interval_to_batch_exports"), + ] + + operations = [ + migrations.RunSQL(TRUNCATE_PERSONOVERRIDE_SQL, "SELECT 1"), + migrations.RemoveConstraint( + model_name="personoverride", + name="unique override per old_person_id", + ), + migrations.RemoveConstraint( + model_name="personoverridemapping", + name="unique_uuid", + ), + migrations.RemoveField( + model_name="personoverridemapping", + name="team_id", + ), + migrations.AddField( + model_name="personoverridemapping", + name="team", + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to="posthog.team"), + preserve_default=False, + ), + # add back constraint that was dropped + migrations.AddConstraint( + model_name="personoverridemapping", + constraint=models.UniqueConstraint(fields=("team_id", "uuid"), name="unique_uuid"), + ), + migrations.AlterField( + model_name="personoverride", + name="old_person_id", + field=models.OneToOneField( + db_column="old_person_id", + on_delete=django.db.models.deletion.CASCADE, + related_name="person_override_old", + to="posthog.personoverridemapping", + ), + ), + # drop the old exclusion constraint + migrations.RunSQL( + "ALTER TABLE posthog_personoverride DROP CONSTRAINT exclude_override_person_id_from_being_old_person_id", + """ + ALTER TABLE posthog_personoverride + ADD CONSTRAINT exclude_override_person_id_from_being_old_person_id + EXCLUDE USING gist((array[old_person_id, override_person_id]) WITH &&, override_person_id WITH <>) + DEFERRABLE + INITIALLY DEFERRED + """, + ), + # add the new exclusion constraint + migrations.RunSQL( + """ + ALTER TABLE posthog_personoverride + ADD CONSTRAINT exclude_override_person_id_from_being_old_person_id + EXCLUDE USING gist (old_person_id WITH =, override_person_id WITH <>) + DEFERRABLE + INITIALLY DEFERRED + """, + "ALTER TABLE posthog_personoverride DROP CONSTRAINT exclude_override_person_id_from_being_old_person_id", + ), + ] diff --git a/posthog/models/person/person.py b/posthog/models/person/person.py index b2b3bb3e36725..0d5045d34e73c 100644 --- a/posthog/models/person/person.py +++ b/posthog/models/person/person.py @@ -119,7 +119,7 @@ class Meta: ] id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID") - team_id = models.BigIntegerField() + team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) uuid = models.UUIDField() @@ -138,7 +138,6 @@ class PersonOverride(models.Model): class Meta: constraints = [ - models.UniqueConstraint(fields=["team", "old_person_id"], name="unique override per old_person_id"), models.CheckConstraint( check=~Q(old_person_id__exact=F("override_person_id")), name="old_person_id_different_from_override_person_id", @@ -148,7 +147,7 @@ class Meta: id = models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID") team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) - old_person_id: models.ForeignKey = models.ForeignKey( + old_person_id: models.OneToOneField = models.OneToOneField( "PersonOverrideMapping", db_column="old_person_id", related_name="person_override_old",