Don't do unique=True with index=True PostgreSQL #1512
Replies: 10 comments 1 reply
-
Hi, What is the migration file that's run? How was it created? |
Beta Was this translation helpful? Give feedback.
-
autogeneration # alembic/env.py
from models.base import Base
target_metadata = Base.metadata |
Beta Was this translation helpful? Give feedback.
-
can you also copy the code of the generated migration? |
Beta Was this translation helpful? Give feedback.
-
there is more field, but I guess its ok """add multiplier boolean field to user
Revision ID: 4331370d3bc8
Revises:
Create Date: 2024-07-30 15:25:39.063423
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '4331370d3bc8'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('telegram_id', sa.BigInteger(), nullable=True),
sa.Column('referral_telegram_id', sa.BigInteger(), nullable=True),
sa.Column('points_count', sa.Integer(), nullable=True),
sa.Column('points_count_for_referral', sa.Integer(), nullable=True),
sa.Column('energy', sa.Integer(), nullable=True),
sa.Column('last_energy_update', sa.DateTime(), nullable=True),
sa.Column('energy_limit', sa.Integer(), nullable=True),
sa.Column('energy_recharging_speed', sa.Integer(), nullable=True),
sa.Column('tap_cost', sa.Integer(), nullable=True),
sa.Column('is_auto_tap_on', sa.Boolean(), nullable=True),
sa.Column('last_auto_tap_claim_date', sa.DateTime(), nullable=True),
sa.Column('multiplier', sa.Boolean(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['referral_telegram_id'], ['users.telegram_id'], ondelete='RESTRICT'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_index(op.f('ix_users_telegram_id'), 'users', ['telegram_id'], unique=True)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_users_telegram_id'), table_name='users')
op.drop_table('users')
# ### end Alembic commands ### if u want to see FULL user model: class User(BaseMixin, Base):
__tablename__ = 'users'
telegram_id = sa.Column(sa.BigInteger, unique=True, index=True)
referral_telegram_id = sa.Column(
sa.BigInteger,
sa.ForeignKey('users.telegram_id', ondelete='RESTRICT'),
nullable=True,
)
referral = sa.orm.relationship(
'User',
remote_side='User.telegram_id',
back_populates='referrals',
)
referrals = sa.orm.relationship('User', back_populates='referral')
points_count = sa.Column(sa.Integer, default=0)
points_count_for_referral = sa.Column(sa.Integer, default=0)
energy = sa.Column(sa.Integer, default=200)
last_energy_update = sa.Column(sa.DateTime, default=datetime.utcnow)
energy_limit = sa.Column(sa.Integer, default=200)
energy_recharging_speed = sa.Column(sa.Integer, default=1)
tap_cost = sa.Column(sa.Integer, default=1)
is_auto_tap_on = sa.Column(sa.Boolean, default=False)
last_auto_tap_claim_date = sa.Column(sa.DateTime, default=datetime.utcnow)
multiplier = sa.Column(sa.Boolean, default=False)
user_leagues = relationship('UserLeague', back_populates='user')
user_missions = sa.orm.relationship('UserMission', back_populates='user')
user_tasks = sa.orm.relationship('UserTask', back_populates='user')
|
Beta Was this translation helpful? Give feedback.
-
It seems to be working as expected. It's generating an unique index. |
Beta Was this translation helpful? Give feedback.
-
My Postgres is not starting with it))) I got exception |
Beta Was this translation helpful? Give feedback.
-
You probably need to move the creating of the fk constraint out of the table creation, using https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.create_foreign_key |
Beta Was this translation helpful? Give feedback.
-
I'm not sure if there is an option to force fk to be created after the table. @zzzeek do you remember if there is an option? |
Beta Was this translation helpful? Give feedback.
-
I looked at the top of the issue to try to understand the problem. I would definitely not do this:
primary key cols are unique already. remove unique=True. then the CREATE TABLE, I dont understand that
where's the "id" column declared? it actually rendered a CREATE TABLE and didn't include the "id" column? that's not what it does on my end, I get the "id":
my own PG gives me a better message, OK it's the telegram_id. You need to add the FK after the fact for that, and no alembic has no automated way to figure out an unusual case like this. |
Beta Was this translation helpful? Give feedback.
-
There's no need to manually create indexes on unique columns; doing so would just duplicate the automatically-created index. source Just remove |
Beta Was this translation helpful? Give feedback.
-
Describe the bug
When defining a column with both unique=True and index=True in SQLAlchemy, the unique constraint seems to be ignored during migration with Alembic. The resulting table does not have a unique constraint on the column, leading to errors when creating foreign key relationships.
Expected behavior
The column should have a unique constraint, and an index should be created when both unique=True and index=True are specified. PostgreSQL should enforce the uniqueness of the column values.
To Reproduce
Error
when I do alembic upgrade head:
Versions
OS: macOs
Python: Python3.11
Alembic: 1.13.2
SQLAlchemy: 2.0.31
Database: PostgreSQL 12.0 on aarch64-unknown-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit
DBAPI: psycopg2
Beta Was this translation helpful? Give feedback.
All reactions