fix: add migration for auth tables
having now fixed the constraint naming convention
This commit is contained in:
parent
1995095627
commit
60d3fcd13b
|
@ -0,0 +1,73 @@
|
||||||
|
"""add users, roles
|
||||||
|
|
||||||
|
Revision ID: d686f7abe3e0
|
||||||
|
Revises: fc3a3bcaa069
|
||||||
|
Create Date: 2024-07-14 13:27:22.703093
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'd686f7abe3e0'
|
||||||
|
down_revision: Union[str, None] = 'fc3a3bcaa069'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# role
|
||||||
|
op.create_table('role',
|
||||||
|
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=100), nullable=False),
|
||||||
|
sa.Column('notes', sa.Text(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('uuid'),
|
||||||
|
sa.UniqueConstraint('name', name=op.f('uq_role_name'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# user
|
||||||
|
op.create_table('user',
|
||||||
|
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('username', sa.String(length=25), nullable=False),
|
||||||
|
sa.Column('password', sa.String(length=60), nullable=True),
|
||||||
|
sa.Column('active', sa.Boolean(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('uuid'),
|
||||||
|
sa.UniqueConstraint('username', name=op.f('uq_user_username'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# permission
|
||||||
|
op.create_table('permission',
|
||||||
|
sa.Column('role_uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('permission', sa.String(length=254), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_permission_role_uuid_role')),
|
||||||
|
sa.PrimaryKeyConstraint('role_uuid', 'permission')
|
||||||
|
)
|
||||||
|
|
||||||
|
# user_x_role
|
||||||
|
op.create_table('user_x_role',
|
||||||
|
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('user_uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('role_uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_user_x_role_role_uuid_role')),
|
||||||
|
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], name=op.f('fk_user_x_role_user_uuid_user')),
|
||||||
|
sa.PrimaryKeyConstraint('uuid')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
|
||||||
|
# user_x_role
|
||||||
|
op.drop_table('user_x_role')
|
||||||
|
|
||||||
|
# permission
|
||||||
|
op.drop_table('permission')
|
||||||
|
|
||||||
|
# user
|
||||||
|
op.drop_table('user')
|
||||||
|
|
||||||
|
# role
|
||||||
|
op.drop_table('role')
|
|
@ -65,16 +65,10 @@ class Role(Base):
|
||||||
See also :attr:`user_refs`.
|
See also :attr:`user_refs`.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'role'
|
__tablename__ = 'role'
|
||||||
__table_args__ = (
|
|
||||||
sa.UniqueConstraint('name',
|
|
||||||
# TODO
|
|
||||||
# name='role_uq_name',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
name = sa.Column(sa.String(length=100), nullable=False, doc="""
|
name = sa.Column(sa.String(length=100), nullable=False, unique=True, doc="""
|
||||||
Name for the role. Each role must have a name, which must be
|
Name for the role. Each role must have a name, which must be
|
||||||
unique.
|
unique.
|
||||||
""")
|
""")
|
||||||
|
@ -86,6 +80,8 @@ class Role(Base):
|
||||||
permission_refs = orm.relationship(
|
permission_refs = orm.relationship(
|
||||||
'Permission',
|
'Permission',
|
||||||
back_populates='role',
|
back_populates='role',
|
||||||
|
# TODO
|
||||||
|
# cascade='save-update, merge, delete, delete-orphan',
|
||||||
doc="""
|
doc="""
|
||||||
List of :class:`Permission` references for the role.
|
List of :class:`Permission` references for the role.
|
||||||
|
|
||||||
|
@ -127,14 +123,8 @@ class Permission(Base):
|
||||||
Represents a permission granted to a role.
|
Represents a permission granted to a role.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'permission'
|
__tablename__ = 'permission'
|
||||||
__table_args__ = (
|
|
||||||
sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'],
|
|
||||||
# TODO
|
|
||||||
# name='permission_fk_role',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
role_uuid = uuid_fk_column(primary_key=True, nullable=False)
|
role_uuid = uuid_fk_column('role.uuid', primary_key=True, nullable=False)
|
||||||
role = orm.relationship(
|
role = orm.relationship(
|
||||||
Role,
|
Role,
|
||||||
back_populates='permission_refs',
|
back_populates='permission_refs',
|
||||||
|
@ -157,18 +147,18 @@ class User(Base):
|
||||||
|
|
||||||
This may or may not correspond to a real person, i.e. some users
|
This may or may not correspond to a real person, i.e. some users
|
||||||
may exist solely for automated tasks.
|
may exist solely for automated tasks.
|
||||||
|
|
||||||
|
.. attribute:: roles
|
||||||
|
|
||||||
|
List of :class:`Role` instances to which the user belongs.
|
||||||
|
|
||||||
|
See also :attr:`role_refs`.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'user'
|
__tablename__ = 'user'
|
||||||
__table_args__ = (
|
|
||||||
sa.UniqueConstraint('username',
|
|
||||||
# TODO
|
|
||||||
# name='user_uq_username',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
username = sa.Column(sa.String(length=25), nullable=False, doc="""
|
username = sa.Column(sa.String(length=25), nullable=False, unique=True, doc="""
|
||||||
Account username. This is required and must be unique.
|
Account username. This is required and must be unique.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@ -186,33 +176,35 @@ class User(Base):
|
||||||
role_refs = orm.relationship(
|
role_refs = orm.relationship(
|
||||||
'UserRole',
|
'UserRole',
|
||||||
back_populates='user',
|
back_populates='user',
|
||||||
|
# TODO
|
||||||
|
# cascade='all, delete-orphan',
|
||||||
doc="""
|
doc="""
|
||||||
List of :class:`UserRole` records.
|
List of :class:`UserRole` instances belonging to the user.
|
||||||
|
|
||||||
|
See also :attr:`roles`.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
roles = association_proxy(
|
||||||
|
'role_refs', 'role',
|
||||||
|
creator=lambda r: UserRole(role=r),
|
||||||
|
# TODO
|
||||||
|
# getset_factory=getset_factory,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username or ""
|
return self.username or ""
|
||||||
|
|
||||||
|
|
||||||
class UserRole(Base):
|
class UserRole(Base):
|
||||||
"""
|
"""
|
||||||
Represents the association between a user and a role.
|
Represents the association between a user and a role; i.e. the
|
||||||
|
user "belongs" or "is assigned" to the role.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'user_x_role'
|
__tablename__ = 'user_x_role'
|
||||||
__table_args__ = (
|
|
||||||
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'],
|
|
||||||
# TODO
|
|
||||||
# name='user_x_role_fk_user',
|
|
||||||
),
|
|
||||||
sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'],
|
|
||||||
# TODO
|
|
||||||
# name='user_x_role_fk_role',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
user_uuid = uuid_fk_column(nullable=False)
|
user_uuid = uuid_fk_column('user.uuid', nullable=False)
|
||||||
user = orm.relationship(
|
user = orm.relationship(
|
||||||
User,
|
User,
|
||||||
back_populates='role_refs',
|
back_populates='role_refs',
|
||||||
|
@ -220,7 +212,7 @@ class UserRole(Base):
|
||||||
Reference to the :class:`User` involved.
|
Reference to the :class:`User` involved.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
role_uuid = uuid_fk_column(nullable=False)
|
role_uuid = uuid_fk_column('role.uuid', nullable=False)
|
||||||
role = orm.relationship(
|
role = orm.relationship(
|
||||||
Role,
|
Role,
|
||||||
back_populates='user_refs',
|
back_populates='user_refs',
|
||||||
|
|
|
@ -34,7 +34,19 @@ from sqlalchemy import orm
|
||||||
from wuttjamaican.util import make_uuid
|
from wuttjamaican.util import make_uuid
|
||||||
|
|
||||||
|
|
||||||
Base = orm.declarative_base()
|
# nb. this convention comes from upstream docs
|
||||||
|
# https://docs.sqlalchemy.org/en/14/core/constraints.html#constraint-naming-conventions
|
||||||
|
naming_convention = {
|
||||||
|
'ix': 'ix_%(column_0_label)s',
|
||||||
|
'uq': 'uq_%(table_name)s_%(column_0_name)s',
|
||||||
|
'ck': 'ck_%(table_name)s_%(constraint_name)s',
|
||||||
|
'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s',
|
||||||
|
'pk': 'pk_%(table_name)s',
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata = sa.MetaData(naming_convention=naming_convention)
|
||||||
|
|
||||||
|
Base = orm.declarative_base(metadata=metadata)
|
||||||
|
|
||||||
|
|
||||||
def uuid_column(*args, **kwargs):
|
def uuid_column(*args, **kwargs):
|
||||||
|
@ -47,11 +59,14 @@ def uuid_column(*args, **kwargs):
|
||||||
return sa.Column(sa.String(length=32), *args, **kwargs)
|
return sa.Column(sa.String(length=32), *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def uuid_fk_column(*args, **kwargs):
|
def uuid_fk_column(target_column, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns a UUID column for use as a foreign key to another table.
|
Returns a UUID column for use as a foreign key to another table.
|
||||||
|
|
||||||
|
:param target_column: Name of the table column on the remote side,
|
||||||
|
e.g. ``'user.uuid'``.
|
||||||
"""
|
"""
|
||||||
return sa.Column(sa.String(length=32), *args, **kwargs)
|
return sa.Column(sa.String(length=32), sa.ForeignKey(target_column), *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Setting(Base):
|
class Setting(Base):
|
||||||
|
|
|
@ -20,7 +20,7 @@ else:
|
||||||
class TestUUIDFKColumn(TestCase):
|
class TestUUIDFKColumn(TestCase):
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
column = model.uuid_column()
|
column = model.uuid_fk_column('foo.bar')
|
||||||
self.assertIsInstance(column, sa.Column)
|
self.assertIsInstance(column, sa.Column)
|
||||||
self.assertIsInstance(column.type, sa.String)
|
self.assertIsInstance(column.type, sa.String)
|
||||||
self.assertEqual(column.type.length, 32)
|
self.assertEqual(column.type.length, 32)
|
||||||
|
|
Loading…
Reference in a new issue