Hi Mike, Thanks for your answer. My problem it’s a classical problem for application development, I want to make authentication and authorisation for a web application. I want to understand why Alembic can’t create the model in autogeneration mode and how modify this model to resolve this problem. With this model I have a join table to provide a many-to-many relationship between users and roles (users group) for example. This allows me to access all roles for one user. For this, I declare a variable roles in users class and I want to make a foreign key with this join table to reach the roles. This joins table has a composite primary key (users.id foreign key and roles.id foreign key) and I don’t know how to make the link (foreign key) with alembic for the users.roles and this composite primary key. When I declare just one element of this primary key, I have the error quoted previously ( there is no unique constraint matching given keys for referenced table "users_roles"). I will send you the CREATE TABLE statements by email in a moment. Best regards, Patrick
Le mer. 12 déc. 2018 à 19:40, Mike Bayer <mike...@zzzcomputing.com> a écrit : > On Wed, Dec 12, 2018 at 1:01 PM patrick payet <papa...@gmail.com> wrote: > > > > I had a SQLAlchemy model like - > > <code> > > 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) > > </code> > > However, Alembic is not generating the correct upgrade, i try to make it > > <code> > > > > 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') > > </code> > > i have an error, primary key for users_roles is a composite keys and i > dont know how declare it, how make that > > it's not clear here if you're saying alembic is adding foreign keys > where they dont belong during autogenerate, or if you dont know why > you are getting the error you are getting. The error is because you > don't need FOREIGN KEY constraints on roles, permissions, or users - > the FOREIGN KEY is only on the many-to-many tables, in this case > users_roles, users_permissions, roles_permissions. > > otherwise if you can illustrate the exact CREATE TABLE statements > you'd like to see, I can illustrate a correct model for that which > Alembic will generate. > > > > > <code> > > sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) there is no > unique constraint matching given keys for referenced table "users_roles" > > [SQL: 'ALTER TABLE users ADD CONSTRAINT users_roles_fk FOREIGN > KEY(roles) REFERENCES users_roles (roles_id) ON DELETE CASCADE'] > (Background on this error at: http://sqlalche.me/e/f405) > > </code> > > > > -- > > You received this message because you are subscribed to the Google > Groups "sqlalchemy-alembic" group. > > To unsubscribe from this group and stop receiving emails from it, send > an email to sqlalchemy-alembic+unsubscr...@googlegroups.com. > > For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy-alembic" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy-alembic+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy-alembic" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy-alembic+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.