1
0
Fork 0

fix: add migration for auth tables

having now fixed the constraint naming convention
This commit is contained in:
Lance Edgar 2024-07-14 14:45:52 -05:00
parent 1995095627
commit 60d3fcd13b
4 changed files with 119 additions and 39 deletions

View file

@ -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')

View file

@ -65,16 +65,10 @@ class Role(Base):
See also :attr:`user_refs`.
"""
__tablename__ = 'role'
__table_args__ = (
sa.UniqueConstraint('name',
# TODO
# name='role_uq_name',
),
)
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
unique.
""")
@ -86,6 +80,8 @@ class Role(Base):
permission_refs = orm.relationship(
'Permission',
back_populates='role',
# TODO
# cascade='save-update, merge, delete, delete-orphan',
doc="""
List of :class:`Permission` references for the role.
@ -127,14 +123,8 @@ class Permission(Base):
Represents a permission granted to a role.
"""
__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,
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
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'
__table_args__ = (
sa.UniqueConstraint('username',
# TODO
# name='user_uq_username',
),
)
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.
""")
@ -186,33 +176,35 @@ class User(Base):
role_refs = orm.relationship(
'UserRole',
back_populates='user',
# TODO
# cascade='all, delete-orphan',
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):
return self.username or ""
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'
__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()
user_uuid = uuid_fk_column(nullable=False)
user_uuid = uuid_fk_column('user.uuid', nullable=False)
user = orm.relationship(
User,
back_populates='role_refs',
@ -220,7 +212,7 @@ class UserRole(Base):
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,
back_populates='user_refs',

View file

@ -34,7 +34,19 @@ from sqlalchemy import orm
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):
@ -47,11 +59,14 @@ def uuid_column(*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.
: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):

View file

@ -20,7 +20,7 @@ else:
class TestUUIDFKColumn(TestCase):
def test_basic(self):
column = model.uuid_column()
column = model.uuid_fk_column('foo.bar')
self.assertIsInstance(column, sa.Column)
self.assertIsInstance(column.type, sa.String)
self.assertEqual(column.type.length, 32)