Alembic migration to convert serial field to integer
Question:
I initially defined one of my SQLAlchemy models as:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True) # surrogate ID
seg_id = db.Column(db.Integer, primary_key=True) # assigned in another system; don't increment
not realizing that seg_id
would become a SERIAL
field in my Postgres database. What I really wanted was an INTEGER
field with a PK constraint (no autoincrement). I turned off autoincrement like so:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True)
seg_id = db.Column(db.Integer, primary_key=True, autoincrement=False) # <--- see here
but the change isn’t reflected when I run migrate
in Alembic. I’ve also tried writing a custom migration with the following operations:
def upgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=False)
def downgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=True)
but that gives the warning autoincrement and existing_autoincrement only make sense for MySQL
. So my question is: is there any way of using Alembic to convert a SERIAL
to an INTEGER
in Postgres?
Answers:
Just set the type explicitly to the one you want. This should work:
op.alter_column('street_segment', 'seg_id', _type=Integer)
I ended up using the following code, basically using raw sql queries, which drops the default value to make it an integer(since serial is integer with a sequence value for default). And for downgrading, the sequence is created and the column’s default is set.
table = "users"
id_column = "id"
def upgrade():
op.execute(f"ALTER TABLE {table} ALTER COLUMN {id_column} DROP DEFAULT")
op.execute(f"DROP SEQUENCE {table}_{id_column}_seq")
# ALTER TABLE yourtable ALTER COLUMN yourcolumn DROP DEFAULT;
def downgrade():
op.execute(f"CREATE SEQUENCE {table}_{id_column}_seq")
op.execute(f"SELECT SETVAL('{table}_{id_column}_seq', (SELECT MAX({id_column})+1 FROM {table}));")
op.execute(f"ALTER TABLE {table} ALTER {id_column} SET DEFAULT nextval('{table}_{id_column}_seq')")
op.execute(f"ALTER SEQUENCE {table}_{id_column}_seq OWNED BY {table}.{id_column}")
I initially defined one of my SQLAlchemy models as:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True) # surrogate ID
seg_id = db.Column(db.Integer, primary_key=True) # assigned in another system; don't increment
not realizing that seg_id
would become a SERIAL
field in my Postgres database. What I really wanted was an INTEGER
field with a PK constraint (no autoincrement). I turned off autoincrement like so:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True)
seg_id = db.Column(db.Integer, primary_key=True, autoincrement=False) # <--- see here
but the change isn’t reflected when I run migrate
in Alembic. I’ve also tried writing a custom migration with the following operations:
def upgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=False)
def downgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=True)
but that gives the warning autoincrement and existing_autoincrement only make sense for MySQL
. So my question is: is there any way of using Alembic to convert a SERIAL
to an INTEGER
in Postgres?
Just set the type explicitly to the one you want. This should work:
op.alter_column('street_segment', 'seg_id', _type=Integer)
I ended up using the following code, basically using raw sql queries, which drops the default value to make it an integer(since serial is integer with a sequence value for default). And for downgrading, the sequence is created and the column’s default is set.
table = "users"
id_column = "id"
def upgrade():
op.execute(f"ALTER TABLE {table} ALTER COLUMN {id_column} DROP DEFAULT")
op.execute(f"DROP SEQUENCE {table}_{id_column}_seq")
# ALTER TABLE yourtable ALTER COLUMN yourcolumn DROP DEFAULT;
def downgrade():
op.execute(f"CREATE SEQUENCE {table}_{id_column}_seq")
op.execute(f"SELECT SETVAL('{table}_{id_column}_seq', (SELECT MAX({id_column})+1 FROM {table}));")
op.execute(f"ALTER TABLE {table} ALTER {id_column} SET DEFAULT nextval('{table}_{id_column}_seq')")
op.execute(f"ALTER SEQUENCE {table}_{id_column}_seq OWNED BY {table}.{id_column}")