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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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