PRIMARY KEY (id), UNIQUE (email) ) CREATE TABLE permissions ( id BIGSERIAL NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(255), PRIMARY KEY (id), UNIQUE (name) )
CREATE TABLE roles ( id BIGSERIAL NOT NULL, name VARCHAR(100) NOT NULL, PRIMARY KEY (id), UNIQUE (name) ) CREATE TABLE users_roles ( users_id BIGINT NOT NULL, roles_id BIGINT NOT NULL, PRIMARY KEY (users_id, roles_id), FOREIGN KEY(users_id) REFERENCES users (id), FOREIGN KEY(roles_id) REFERENCES roles (id) ) CREATE TABLE roles_permissions ( roles_id BIGINT NOT NULL, permissions_id BIGINT NOT NULL, PRIMARY KEY (roles_id, permissions_id), FOREIGN KEY(roles_id) REFERENCES roles (id), FOREIGN KEY(permissions_id) REFERENCES permissions (id) ) CREATE TABLE users_permissions ( users_id BIGINT NOT NULL, permissions_id BIGINT NOT NULL, PRIMARY KEY (users_id, permissions_id), FOREIGN KEY(users_id) REFERENCES users (id), FOREIGN KEY(permissions_id) REFERENCES permissions (id) ) My mistake is to have mixed in my mind the information of the object mapping and the relational constraints of the database. But why Alembic can’t create this model in auto-generation mode? Best regards Le jeu. 13 déc. 2018 à 14:44, Mike Bayer <mike...@zzzcomputing.com> a écrit : > On Thu, Dec 13, 2018 at 7:11 AM patrick payet <papa...@gmail.com> wrote: > > > > 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. > > the link is the user_roles table and the FOREIGN KEY constraints it > has between users and roles tables. you can't locate a unique row > in user_roles given only the user table because it has no column that > refers to user_roles.roles_id. at least I'm trying to parse your > words as given. however CREATE TABLE statements will show exactly > what you mean in case it is something different that just happens to > sound the same when described. > > 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. > > yup > > > > > 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. > > -- > 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.