feat: add model for Person; tie to User
This commit is contained in:
parent
60d3fcd13b
commit
43ca404837
|
@ -0,0 +1,45 @@
|
||||||
|
"""add people
|
||||||
|
|
||||||
|
Revision ID: 3abcc44f7f91
|
||||||
|
Revises: d686f7abe3e0
|
||||||
|
Create Date: 2024-07-14 15:14:30.552682
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '3abcc44f7f91'
|
||||||
|
down_revision: Union[str, None] = 'd686f7abe3e0'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# person
|
||||||
|
op.create_table('person',
|
||||||
|
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('full_name', sa.String(length=100), nullable=False),
|
||||||
|
sa.Column('first_name', sa.String(length=50), nullable=True),
|
||||||
|
sa.Column('middle_name', sa.String(length=50), nullable=True),
|
||||||
|
sa.Column('last_name', sa.String(length=50), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_person'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# user
|
||||||
|
op.add_column('user', sa.Column('person_uuid', sa.String(length=32), nullable=True))
|
||||||
|
op.create_foreign_key(op.f('fk_user_person_uuid_person'), 'user', 'person', ['person_uuid'], ['uuid'])
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
|
||||||
|
# user
|
||||||
|
op.drop_constraint(op.f('fk_user_person_uuid_person'), 'user', type_='foreignkey')
|
||||||
|
op.drop_column('user', 'person_uuid')
|
||||||
|
|
||||||
|
# person
|
||||||
|
op.drop_table('person')
|
|
@ -31,11 +31,12 @@ The ``wuttjamaican.db.model`` namespace contains the following:
|
||||||
* :func:`~wuttjamaican.db.model.base.uuid_fk_column()`
|
* :func:`~wuttjamaican.db.model.base.uuid_fk_column()`
|
||||||
* :class:`~wuttjamaican.db.model.base.Base`
|
* :class:`~wuttjamaican.db.model.base.Base`
|
||||||
* :class:`~wuttjamaican.db.model.base.Setting`
|
* :class:`~wuttjamaican.db.model.base.Setting`
|
||||||
|
* :class:`~wuttjamaican.db.model.base.Person`
|
||||||
* :class:`~wuttjamaican.db.model.auth.Role`
|
* :class:`~wuttjamaican.db.model.auth.Role`
|
||||||
* :class:`~wuttjamaican.db.model.auth.Permission`
|
* :class:`~wuttjamaican.db.model.auth.Permission`
|
||||||
* :class:`~wuttjamaican.db.model.auth.User`
|
* :class:`~wuttjamaican.db.model.auth.User`
|
||||||
* :class:`~wuttjamaican.db.model.auth.UserRole`
|
* :class:`~wuttjamaican.db.model.auth.UserRole`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .base import Base, Setting, uuid_column, uuid_fk_column
|
from .base import uuid_column, uuid_fk_column, Base, Setting, Person
|
||||||
from .auth import Role, Permission, User, UserRole
|
from .auth import Role, Permission, User, UserRole
|
||||||
|
|
|
@ -166,6 +166,17 @@ class User(Base):
|
||||||
Hashed password for login. (The raw password is not stored.)
|
Hashed password for login. (The raw password is not stored.)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
person_uuid = uuid_fk_column('person.uuid', nullable=True)
|
||||||
|
person = orm.relationship(
|
||||||
|
'Person',
|
||||||
|
# TODO: seems like this is not needed?
|
||||||
|
# uselist=False,
|
||||||
|
back_populates='users',
|
||||||
|
doc="""
|
||||||
|
Reference to the :class:`~wuttjamaican.db.model.base.Person`
|
||||||
|
whose user account this is.
|
||||||
|
""")
|
||||||
|
|
||||||
active = sa.Column(sa.Boolean(), nullable=False, default=True, doc="""
|
active = sa.Column(sa.Boolean(), nullable=False, default=True, doc="""
|
||||||
Flag indicating whether the user account is "active" - it is
|
Flag indicating whether the user account is "active" - it is
|
||||||
``True`` by default.
|
``True`` by default.
|
||||||
|
@ -192,6 +203,10 @@ class User(Base):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if self.person:
|
||||||
|
name = str(self.person)
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
return self.username or ""
|
return self.username or ""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -85,3 +85,78 @@ class Setting(Base):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name or ""
|
return self.name or ""
|
||||||
|
|
||||||
|
|
||||||
|
class Person(Base):
|
||||||
|
"""
|
||||||
|
Represents a person.
|
||||||
|
|
||||||
|
The use for this table in the base framework, is to associate with
|
||||||
|
a :class:`~wuttjamaican.db.model.auth.User` to provide first and
|
||||||
|
last name etc. (However a user does not have to be associated
|
||||||
|
with any person.)
|
||||||
|
|
||||||
|
But this table could also be used as a basis for a Customer or
|
||||||
|
Employee relationship etc.
|
||||||
|
"""
|
||||||
|
__tablename__ = 'person'
|
||||||
|
|
||||||
|
uuid = uuid_column()
|
||||||
|
|
||||||
|
full_name = sa.Column(sa.String(length=100), nullable=False, doc="""
|
||||||
|
Full name for the person. Note that this is *required*.
|
||||||
|
""")
|
||||||
|
|
||||||
|
first_name = sa.Column(sa.String(length=50), nullable=True, doc="""
|
||||||
|
The person's first name.
|
||||||
|
""")
|
||||||
|
|
||||||
|
middle_name = sa.Column(sa.String(length=50), nullable=True, doc="""
|
||||||
|
The person's middle name or initial.
|
||||||
|
""")
|
||||||
|
|
||||||
|
last_name = sa.Column(sa.String(length=50), nullable=True, doc="""
|
||||||
|
The person's last name.
|
||||||
|
""")
|
||||||
|
|
||||||
|
users = orm.relationship(
|
||||||
|
'User',
|
||||||
|
back_populates='person',
|
||||||
|
# TODO
|
||||||
|
# cascade_backrefs=False,
|
||||||
|
doc="""
|
||||||
|
List of :class:`~wuttjamaican.db.model.auth.User` accounts for
|
||||||
|
the person. Typically there is only one user account per
|
||||||
|
person, but technically multiple are supported.
|
||||||
|
""")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.full_name or ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user(self):
|
||||||
|
"""
|
||||||
|
Reference to the "first"
|
||||||
|
:class:`~wuttjamaican.db.model.auth.User` account for the
|
||||||
|
person, or ``None``.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Note that the database schema supports multiple users per
|
||||||
|
person, but this property logic ignores that and will only
|
||||||
|
ever return "one or none". That might be fine in 99% of
|
||||||
|
cases, but if multiple accounts exist for a person, the one
|
||||||
|
returned is indeterminate.
|
||||||
|
|
||||||
|
See :attr:`users` to access the full list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: i'm not crazy about the ambiguity here re: number of
|
||||||
|
# user accounts a person may have. in particular it's not
|
||||||
|
# clear *which* user account would be returned, as there is no
|
||||||
|
# sequence ordinal defined etc. a better approach might be to
|
||||||
|
# force callers to assume the possibility of multiple
|
||||||
|
# user accounts per person? (if so, remove this property)
|
||||||
|
|
||||||
|
if self.users:
|
||||||
|
return self.users[0]
|
||||||
|
|
|
@ -5,6 +5,7 @@ from unittest import TestCase
|
||||||
try:
|
try:
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from wuttjamaican.db.model import auth as model
|
from wuttjamaican.db.model import auth as model
|
||||||
|
from wuttjamaican.db.model.base import Person
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -29,8 +30,16 @@ else:
|
||||||
|
|
||||||
class TestUser(TestCase):
|
class TestUser(TestCase):
|
||||||
|
|
||||||
def test_basic(self):
|
def test_str(self):
|
||||||
user = model.User()
|
user = model.User()
|
||||||
self.assertEqual(str(user), "")
|
self.assertEqual(str(user), "")
|
||||||
user.username = 'barney'
|
user.username = 'barney'
|
||||||
self.assertEqual(str(user), "barney")
|
self.assertEqual(str(user), "barney")
|
||||||
|
|
||||||
|
def test_str_with_person(self):
|
||||||
|
user = model.User()
|
||||||
|
self.assertEqual(str(user), "")
|
||||||
|
|
||||||
|
person = Person(full_name="Barney Rubble")
|
||||||
|
user.person = person
|
||||||
|
self.assertEqual(str(user), "Barney Rubble")
|
||||||
|
|
|
@ -5,6 +5,7 @@ from unittest import TestCase
|
||||||
try:
|
try:
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from wuttjamaican.db.model import base as model
|
from wuttjamaican.db.model import base as model
|
||||||
|
from wuttjamaican.db.model.auth import User
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -32,3 +33,19 @@ else:
|
||||||
self.assertEqual(str(setting), "")
|
self.assertEqual(str(setting), "")
|
||||||
setting.name = 'foo'
|
setting.name = 'foo'
|
||||||
self.assertEqual(str(setting), "foo")
|
self.assertEqual(str(setting), "foo")
|
||||||
|
|
||||||
|
class TestPerson(TestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
person = model.Person()
|
||||||
|
self.assertEqual(str(person), "")
|
||||||
|
person.full_name = "Barney Rubble"
|
||||||
|
self.assertEqual(str(person), "Barney Rubble")
|
||||||
|
|
||||||
|
def test_users(self):
|
||||||
|
person = model.Person()
|
||||||
|
self.assertIsNone(person.user)
|
||||||
|
|
||||||
|
user = User()
|
||||||
|
person.users.append(user)
|
||||||
|
self.assertIs(person.user, user)
|
||||||
|
|
Loading…
Reference in a new issue