239 lines
8.6 KiB
Python
239 lines
8.6 KiB
Python
# Convert HQ sites.site_id from bigint to farm-matching slug (varchar).
|
|
|
|
from django.db import migrations, models
|
|
import django.db.models.deletion
|
|
|
|
|
|
def _unique_slug(candidate: str, used: set[str]) -> str:
|
|
final = candidate[:64]
|
|
n = 0
|
|
while final in used or not final:
|
|
n += 1
|
|
suffix = f"-{n}"
|
|
final = f"{candidate[: 64 - len(suffix)]}{suffix}"
|
|
used.add(final)
|
|
return final
|
|
|
|
|
|
def forwards_convert_site_pk(apps, schema_editor):
|
|
from django.utils.text import slugify
|
|
|
|
connection = schema_editor.connection
|
|
Site = apps.get_model("farms", "Site")
|
|
Kandang = apps.get_model("farms", "Kandang")
|
|
ActiveSite = apps.get_model("sync", "ActiveSite")
|
|
|
|
sites = list(
|
|
Site.objects.all().values(
|
|
"site_id",
|
|
"site_name",
|
|
"user_id",
|
|
"active_site_id",
|
|
"created_at",
|
|
"updated_at",
|
|
)
|
|
)
|
|
kandangs = list(
|
|
Kandang.objects.all().values(
|
|
"kandang_id",
|
|
"kandang_name",
|
|
"feed_in_button_urls",
|
|
"site_id",
|
|
"created_at",
|
|
"updated_at",
|
|
)
|
|
)
|
|
active_source = {
|
|
row.pk: str(row.source_site_id).strip()
|
|
for row in ActiveSite.objects.all()
|
|
if str(row.source_site_id or "").strip()
|
|
}
|
|
|
|
used: set[str] = set()
|
|
mapping: dict[int, str] = {}
|
|
for row in sites:
|
|
old = row["site_id"]
|
|
candidate = active_source.get(row["active_site_id"]) or ""
|
|
if not candidate:
|
|
base = slugify(row["site_name"] or "")[:40] or "site"
|
|
candidate = f"{base}-legacy-{old}"
|
|
mapping[old] = _unique_slug(candidate, used)
|
|
|
|
with connection.cursor() as cursor:
|
|
if connection.vendor == "sqlite":
|
|
cursor.execute("PRAGMA foreign_keys=OFF")
|
|
cursor.execute(
|
|
"""
|
|
CREATE TABLE sites_new (
|
|
site_id varchar(64) NOT NULL PRIMARY KEY,
|
|
site_name varchar(30) NOT NULL,
|
|
user_id bigint NOT NULL REFERENCES user_access (user_id)
|
|
DEFERRABLE INITIALLY DEFERRED,
|
|
active_site_id bigint UNIQUE REFERENCES active_sites (active_site_id)
|
|
DEFERRABLE INITIALLY DEFERRED,
|
|
created_at datetime NOT NULL,
|
|
updated_at datetime NOT NULL
|
|
)
|
|
"""
|
|
)
|
|
for row in sites:
|
|
cursor.execute(
|
|
"""
|
|
INSERT INTO sites_new
|
|
(site_id, site_name, user_id, active_site_id, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
""",
|
|
[
|
|
mapping[row["site_id"]],
|
|
row["site_name"],
|
|
row["user_id"],
|
|
row["active_site_id"],
|
|
row["created_at"],
|
|
row["updated_at"],
|
|
],
|
|
)
|
|
cursor.execute("DROP TABLE sites")
|
|
cursor.execute("ALTER TABLE sites_new RENAME TO sites")
|
|
cursor.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_sites_user_id ON sites (user_id)"
|
|
)
|
|
|
|
cursor.execute(
|
|
"""
|
|
CREATE TABLE kandang_new (
|
|
kandang_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
kandang_name varchar(30) NOT NULL,
|
|
feed_in_button_urls text NOT NULL DEFAULT '[]',
|
|
site_id varchar(64) NOT NULL REFERENCES sites (site_id)
|
|
DEFERRABLE INITIALLY DEFERRED,
|
|
created_at datetime NOT NULL,
|
|
updated_at datetime NOT NULL
|
|
)
|
|
"""
|
|
)
|
|
import json
|
|
|
|
for row in kandangs:
|
|
urls = row["feed_in_button_urls"]
|
|
if not isinstance(urls, str):
|
|
urls = json.dumps(urls if urls is not None else [])
|
|
cursor.execute(
|
|
"""
|
|
INSERT INTO kandang_new
|
|
(kandang_id, kandang_name, feed_in_button_urls, site_id,
|
|
created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
""",
|
|
[
|
|
row["kandang_id"],
|
|
row["kandang_name"],
|
|
urls,
|
|
mapping[row["site_id"]],
|
|
row["created_at"],
|
|
row["updated_at"],
|
|
],
|
|
)
|
|
cursor.execute("DROP TABLE kandang")
|
|
cursor.execute("ALTER TABLE kandang_new RENAME TO kandang")
|
|
cursor.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_kandang_site_id ON kandang (site_id)"
|
|
)
|
|
cursor.execute("PRAGMA foreign_keys=ON")
|
|
elif connection.vendor == "postgresql":
|
|
cursor.execute(
|
|
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS site_id_new varchar(64)"
|
|
)
|
|
for old, new in mapping.items():
|
|
cursor.execute(
|
|
"UPDATE sites SET site_id_new = %s WHERE site_id = %s",
|
|
[new, old],
|
|
)
|
|
cursor.execute(
|
|
"ALTER TABLE kandang ADD COLUMN IF NOT EXISTS site_id_new varchar(64)"
|
|
)
|
|
cursor.execute(
|
|
"""
|
|
UPDATE kandang AS k
|
|
SET site_id_new = s.site_id_new
|
|
FROM sites AS s
|
|
WHERE k.site_id = s.site_id
|
|
"""
|
|
)
|
|
cursor.execute(
|
|
"""
|
|
ALTER TABLE kandang DROP CONSTRAINT IF EXISTS kandang_site_id_fkey
|
|
"""
|
|
)
|
|
# Django default name variants
|
|
cursor.execute(
|
|
"""
|
|
DO $$
|
|
DECLARE r RECORD;
|
|
BEGIN
|
|
FOR r IN
|
|
SELECT conname FROM pg_constraint
|
|
WHERE conrelid = 'kandang'::regclass
|
|
AND contype = 'f'
|
|
AND pg_get_constraintdef(oid) ILIKE '%%site_id%%'
|
|
LOOP
|
|
EXECUTE format('ALTER TABLE kandang DROP CONSTRAINT %I', r.conname);
|
|
END LOOP;
|
|
END $$;
|
|
"""
|
|
)
|
|
cursor.execute("ALTER TABLE kandang DROP COLUMN site_id")
|
|
cursor.execute(
|
|
"ALTER TABLE kandang RENAME COLUMN site_id_new TO site_id"
|
|
)
|
|
cursor.execute("ALTER TABLE sites DROP CONSTRAINT sites_pkey")
|
|
cursor.execute("ALTER TABLE sites DROP COLUMN site_id")
|
|
cursor.execute("ALTER TABLE sites RENAME COLUMN site_id_new TO site_id")
|
|
cursor.execute("ALTER TABLE sites ALTER COLUMN site_id SET NOT NULL")
|
|
cursor.execute("ALTER TABLE sites ADD PRIMARY KEY (site_id)")
|
|
cursor.execute("ALTER TABLE kandang ALTER COLUMN site_id SET NOT NULL")
|
|
cursor.execute(
|
|
"""
|
|
ALTER TABLE kandang
|
|
ADD CONSTRAINT kandang_site_id_fkey
|
|
FOREIGN KEY (site_id) REFERENCES sites (site_id) ON DELETE CASCADE
|
|
"""
|
|
)
|
|
cursor.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_kandang_site_id ON kandang (site_id)"
|
|
)
|
|
else:
|
|
raise NotImplementedError(
|
|
f"sites.site_id slug migration unsupported for {connection.vendor}"
|
|
)
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
dependencies = [
|
|
("farms", "0018_rename_city_site_to_active_site"),
|
|
("sync", "0007_uniq_source_site_id"),
|
|
]
|
|
|
|
operations = [
|
|
migrations.SeparateDatabaseAndState(
|
|
database_operations=[
|
|
migrations.RunPython(forwards_convert_site_pk, migrations.RunPython.noop),
|
|
],
|
|
state_operations=[
|
|
migrations.AlterField(
|
|
model_name="site",
|
|
name="site_id",
|
|
field=models.SlugField(max_length=64, primary_key=True, serialize=False),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="kandang",
|
|
name="site",
|
|
field=models.ForeignKey(
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="kandangs",
|
|
to="farms.site",
|
|
),
|
|
),
|
|
],
|
|
),
|
|
]
|