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()`
|
||||
* :class:`~wuttjamaican.db.model.base.Base`
|
||||
* :class:`~wuttjamaican.db.model.base.Setting`
|
||||
* :class:`~wuttjamaican.db.model.base.Person`
|
||||
* :class:`~wuttjamaican.db.model.auth.Role`
|
||||
* :class:`~wuttjamaican.db.model.auth.Permission`
|
||||
* :class:`~wuttjamaican.db.model.auth.User`
|
||||
* :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
|
||||
|
|
|
@ -166,6 +166,17 @@ class User(Base):
|
|||
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="""
|
||||
Flag indicating whether the user account is "active" - it is
|
||||
``True`` by default.
|
||||
|
@ -192,6 +203,10 @@ class User(Base):
|
|||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.person:
|
||||
name = str(self.person)
|
||||
if name:
|
||||
return name
|
||||
return self.username or ""
|
||||
|
||||
|
||||
|
|
|
@ -85,3 +85,78 @@ class Setting(Base):
|
|||
|
||||
def __str__(self):
|
||||
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:
|
||||
import sqlalchemy as sa
|
||||
from wuttjamaican.db.model import auth as model
|
||||
from wuttjamaican.db.model.base import Person
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
|
@ -29,8 +30,16 @@ else:
|
|||
|
||||
class TestUser(TestCase):
|
||||
|
||||
def test_basic(self):
|
||||
def test_str(self):
|
||||
user = model.User()
|
||||
self.assertEqual(str(user), "")
|
||||
user.username = '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:
|
||||
import sqlalchemy as sa
|
||||
from wuttjamaican.db.model import base as model
|
||||
from wuttjamaican.db.model.auth import User
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
|
@ -32,3 +33,19 @@ else:
|
|||
self.assertEqual(str(setting), "")
|
||||
setting.name = '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