migrate site id to varchar instead of int
This commit is contained in:
1 parent
61f954a3bd
commit
36639d8c38
16 files changed
+448
-41
No files matched your search
@@ -0,0 +1,238 @@
|
||||
# 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",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
Reference in new issue
Block a user