Skip to content

Commit 60bf2b7

Browse files
rebase migrations
1 parent d7a6cb5 commit 60bf2b7

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Generated by Django 5.1.13 on 2025-11-01 17:04
2+
3+
import django.db.models.deletion
4+
import pgtrigger.compiler
5+
import pgtrigger.migrations
6+
from django.db import migrations, models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
("dojo", "0247_remove_finding_insert_insert_and_more")
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="FindingReviewers",
18+
fields=[
19+
],
20+
options={
21+
"proxy": True,
22+
"indexes": [],
23+
"constraints": [],
24+
},
25+
bases=("dojo.finding_reviewers",),
26+
),
27+
migrations.CreateModel(
28+
name="FindingReviewersEvent",
29+
fields=[
30+
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
31+
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
32+
("pgh_label", models.TextField(help_text="The event label.")),
33+
("id", models.IntegerField()),
34+
("dojo_user", models.ForeignKey(db_constraint=False, db_index=False, db_tablespace="", on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", related_query_name="+", to="dojo.dojo_user")),
35+
("finding", models.ForeignKey(db_constraint=False, db_index=False, db_tablespace="", on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", related_query_name="+", to="dojo.finding")),
36+
("pgh_context", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="pghistory.context")),
37+
("pgh_obj", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="events", to="dojo.findingreviewers")),
38+
],
39+
options={
40+
"abstract": False,
41+
"db_table": "dojo_finding_reviewersevent",
42+
},
43+
),
44+
migrations.AddIndex(
45+
model_name="findingreviewersevent",
46+
index=models.Index(fields=["pgh_created_at"], name="dojo_findin_pgh_cre_d5e5b4_idx"),
47+
),
48+
migrations.AddIndex(
49+
model_name="findingreviewersevent",
50+
index=models.Index(fields=["pgh_label"], name="dojo_findin_pgh_lab_5517f9_idx"),
51+
),
52+
migrations.AddIndex(
53+
model_name="findingreviewersevent",
54+
index=models.Index(fields=["pgh_context_id"], name="dojo_findin_pgh_con_06229b_idx"),
55+
),
56+
pgtrigger.migrations.AddTrigger(
57+
model_name="findingreviewers",
58+
trigger=pgtrigger.compiler.Trigger(name="insert_insert", sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_finding_reviewersevent" ("dojo_user_id", "finding_id", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."dojo_user_id", NEW."finding_id", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id"); RETURN NULL;', hash="5c1fd440159e49c929122cbb590f96983a1c934e", operation="INSERT", pgid="pgtrigger_insert_insert_0808c", table="dojo_finding_reviewers", when="AFTER")),
59+
),
60+
pgtrigger.migrations.AddTrigger(
61+
model_name="findingreviewers",
62+
trigger=pgtrigger.compiler.Trigger(name="delete_delete", sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_finding_reviewersevent" ("dojo_user_id", "finding_id", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (OLD."dojo_user_id", OLD."finding_id", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id"); RETURN NULL;', hash="23a4e01eaea469f708679392a6a92a6e16b21181", operation="DELETE", pgid="pgtrigger_delete_delete_40083", table="dojo_finding_reviewers", when="AFTER")),
63+
),
64+
]
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Generated manually for pghistory initial backfill
2+
3+
import logging
4+
5+
from django.conf import settings
6+
from django.db import migrations
7+
8+
from dojo.auditlog import (
9+
get_tracked_models,
10+
process_model_backfill,
11+
)
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
def backfill_pghistory_tables(apps, schema_editor):
17+
"""
18+
Backfill pghistory tables with initial snapshots of existing records.
19+
20+
This migration is fail-safe: if it fails for some reason, it will continue
21+
where it left off on the next run, as it only processes records that don't
22+
already have initial_backfill events.
23+
"""
24+
# Skip if auditlog is not enabled
25+
if not settings.ENABLE_AUDITLOG:
26+
logger.info("pghistory is not enabled. Skipping backfill.")
27+
return
28+
29+
# Check if we can use COPY (PostgreSQL only)
30+
if settings.DATABASES["default"]["ENGINE"] != "django.db.backends.postgresql":
31+
logger.warning(
32+
"COPY operations only available with PostgreSQL. "
33+
"Skipping backfill. Use the pghistory_backfill command instead.",
34+
)
35+
return
36+
37+
# Progress callback for migration logging
38+
def progress_callback(msg, style=None):
39+
"""Progress callback that logs to Django's logger."""
40+
if style == "ERROR":
41+
logger.error(msg)
42+
elif style == "WARNING":
43+
logger.warning(msg)
44+
elif style == "SUCCESS":
45+
logger.info(msg)
46+
else:
47+
logger.info(msg)
48+
49+
# Get all tracked models
50+
tracked_models = get_tracked_models()
51+
52+
logger.info(f"Starting pghistory backfill for {len(tracked_models)} model(s)...")
53+
54+
total_processed = 0
55+
for model_name in tracked_models:
56+
logger.info(f"Processing {model_name}...")
57+
try:
58+
processed, _ = process_model_backfill(
59+
model_name=model_name,
60+
batch_size=10000,
61+
dry_run=False,
62+
progress_callback=progress_callback,
63+
)
64+
total_processed += processed
65+
except Exception as e:
66+
logger.error(f"Failed to backfill {model_name}: {e}", exc_info=True)
67+
# Continue with other models even if one fails
68+
continue
69+
70+
logger.info(f"Pghistory backfill complete: Processed {total_processed:,} records")
71+
72+
73+
class Migration(migrations.Migration):
74+
75+
dependencies = [
76+
("dojo", "0248_findingreviewers_findingreviewersevent_and_more"),
77+
]
78+
79+
operations = [
80+
migrations.RunPython(
81+
backfill_pghistory_tables,
82+
reverse_code=migrations.RunPython.noop,
83+
),
84+
]
85+

0 commit comments

Comments
 (0)