alembic: связь многих со многими с внешним ключом к составному первичному ключу - PullRequest
0 голосов
/ 12 декабря 2018

У меня была модель SQLAlchemy, например -

class User(DeclarativeBase):
    __tablename__ = 'users'
    id = Column(BigInteger, primary_key=True)
    email = Column(String(100), unique=True, nullable=False)
    name = Column(String(100), nullable=False)
    hashed_password = Column(String(100), nullable=False)
    is_admin = Column(BOOLEAN, default=False)
    is_active = Column(BOOLEAN, default=True)
    created = Column(DateTime, default=datetime.now)
    modified = Column(DateTime, default=datetime.now, onpudate=datetime.datetime.now)
    roles = relationship('Role', secondary=users_roles, back_populates='users', cascade="all, delete-orphan")
    permissions = relationship('Permission', secondary=users_permissions, back_populates='users',
                               cascade="all, delete-orphan")

    def __repr__(self):
        return "<User(name='%s', email='%s', hashed_password='%s')>" % (self.name, self.email, self.hashed_password)


class Role(DeclarativeBase):
    __tablename__ = 'roles'

    id = Column(BigInteger, primary_key=True)
    name = Column(String(100), unique=True, nullable=False)
    users = relationship('User', secondary=users_roles, back_populates='roles',cascade="all, delete-orphan")
    permissions = relationship('Permission', secondary=roles_permissions, back_populates='roles',
                               cascade="all, delete-orphan")

    def __repr__(self):
        return "<Role(name='%s')>" % self.name


class Permission(DeclarativeBase):
    __tablename__ = 'permissions'
    id = Column(BigInteger, primary_key=True)
    name = Column(String(100), unique=True, nullable=False)
    description = Column(String(255))
    users = relationship('User', secondary=users_permissions, back_populates='permissions',
                         cascade="all, delete-orphan")
    roles = relationship('Role', secondary=roles_permissions, back_populates='permissions',
                         cascade="all, delete-orphan")

    def __repr__(self):
        return "<Permission(name='%s', description='%s')>" % (self.name, self.description)

Однако, Alembic не генерирует правильное обновление, я пытаюсь сделать это

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # op.drop_table('users')
    # ### end Alembic commands ###

    op.create_table('roles',
                    sa.Column('id', sa.BIGINT(), autoincrement=True, nullable=False),
                    sa.Column('name', sa.String(100), autoincrement=False, nullable=False),
                    sa.Column('users', BigInteger),
                    sa.Column('permissions', BigInteger),
                    sa.PrimaryKeyConstraint('id'),
                    sa.UniqueConstraint('name', name='roles_name_key')
                    # sa.ForeignKeyConstraint(['users'], ['users_roles.users_id'], ondelete='CASCADE'),
                    # sa.ForeignKeyConstraint(['permissions'], ['roles_permissions.permissions_id'], ondelete='CASCADE')
                    )
    op.create_table('permissions',
                    sa.Column('id', sa.BIGINT(), autoincrement=True, nullable=False),
                    sa.Column('name', sa.String(100), autoincrement=False, nullable=False),
                    sa.Column('description', sa.String(255)),
                    sa.Column('users', BigInteger),
                    sa.Column('roles', BigInteger),
                    sa.PrimaryKeyConstraint('id')
                    # sa.ForeignKeyConstraint(['users'], ['users_permissions.users_id'], ondelete='CASCADE'),
                    # sa.ForeignKeyConstraint(['roles'], ['role_permissions.roles_id'], ondelete='CASCADE')
                    )
    op.create_table('users_roles',
                    sa.Column('users_id', BigInteger, sa.ForeignKey('users.id'), primary_key=True),
                    sa.Column('roles_id', BigInteger, sa.ForeignKey('roles.id'), primary_key=True)
                    )
    op.create_table('users_permissions',
                    sa.Column('users_id', BigInteger, sa.ForeignKey('users.id'), primary_key=True),
                    sa.Column('permissions_id', BigInteger, sa.ForeignKey('permissions.id'), primary_key=True)
                    )
    op.create_table('roles_permissions',
                    sa.Column('roles_id', BigInteger, sa.ForeignKey('roles.id'), primary_key=True),
                    sa.Column('permissions_id', BigInteger, sa.ForeignKey('permissions.id'), primary_key=True)
                    )
    op.drop_constraint('users_name_key', 'users')
    op.add_column('users', sa.Column('is_admin', sa.BOOLEAN, autoincrement=False, nullable=False, default=False))
    op.add_column('users', sa.Column('is_active', sa.BOOLEAN, autoincrement=False, nullable=False, default=True))
    op.add_column('users', sa.Column('created', sa.DateTime, autoincrement=False, nullable=False,
                                     default=datetime.datetime.now))
    op.add_column('users', sa.Column('modified', sa.DateTime, autoincrement=False, nullable=False,
                                     default=datetime.datetime.now))
    op.add_column('users', sa.Column('roles', BigInteger))
    op.add_column('users', sa.Column('permissions', BigInteger))

    op.create_foreign_key(constraint_name="users_roles_fk", source_table="users", referent_table="users_roles",
                          local_cols=["roles"], remote_cols=["roles_id"], ondelete='CASCADE')
    op.create_foreign_key("users_permissions_fk", "users", "users_permissions", ["permissions"], ["permissions_id"],ondelete='CASCADE')
    op.create_foreign_key("permissions_users_fk", "permissions", "users_permissions", ["users"], ["users_id"],
                          ondelete='CASCADE')
    op.create_foreign_key("permissions_roles_fk", "permissions", "roles_permissions", ["roles"], ["roles_id"],
                          ondelete='CASCADE')
    op.create_foreign_key("roles_users_fk", "roles", "users_roles", ["users"], ["users_id"],
                          ondelete='CASCADE')
    op.create_foreign_key("roles_permissions_fk", "roles", "roles_permissions", ["permissions"], ["permissions_id"],
                          ondelete='CASCADE')

У меня ошибка, первичный ключfor users_roles - это составные ключи, и я не знаю, как его объявить, как сделать это. sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) не существует уникального ограничения, соответствующего заданным ключам для ссылочной таблицы "users_roles" [SQL: 'ALTER TABLE users ADDCONSTRAINT users_roles_fk FOREIGN KEY (role) ССЫЛКИ users_roles (role_id) ON DELETE CASCADE ']

...