Ok, may be if I provide an example you could understand it a bit clearer. Django makemigration generate this kind of migration:
migrations.AlterField(
model_name="foo",
name="status",
field=models.ForeignKey(
db_default=1,
on_delete=django.db.models.deletion.RESTRICT,
to="api.foo_statuses",
verbose_name="Foo status",
),
),
And I use that SQL alternative to migrate manually:
migrations.RunSQL("""ALTER TABLE api_foo RENAME status TO status_id;""",
"""ALTER TABLE api_foo RENAME status_id TO status;"""),
migrations.RunSQL(
f"""
ALTER TABLE api_foo
ALTER COLUMN status_id
TYPE bigint
USING (
CASE status_id
{" ".join(f"WHEN {sn!r} THEN {sid}" for sid, sn in STATUS_REL.items())}
ELSE 1
END
);
""",
f"""
ALTER TABLE api_foo
ALTER COLUMN status_id
TYPE character varying
USING (
CASE status_id
{" ".join(f"WHEN {sid} THEN {sn!r}" for sid, sn in STATUS_REL.items())}
ELSE 'initial_status'
END
);
"""),
migrations.RunSQL("""ALTER TABLE ONLY api_foo ALTER COLUMN status_id SET DEFAULT 1;""",
"""ALTER TABLE ONLY api_foo ALTER COLUMN status_id SET DEFAULT 'initial_status';"""),
migrations.RunSQL(
"""
ALTER TABLE api_foo
ADD CONSTRAINT api_foo_status_id_00000000_fk_api_status_id FOREIGN KEY (status_id)
REFERENCES public.api_foo_statuses (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE RESTRICT
DEFERRABLE INITIALLY DEFERRED;
""",
"""
ALTER TABLE api_foo
DROP CONSTRAINT api_foo_status_id_00000000_fk_api_status_id;
"""),