1
0
Fork 0

feat: add model for Person; tie to User

This commit is contained in:
Lance Edgar 2024-07-14 15:47:39 -05:00
parent 60d3fcd13b
commit 43ca404837
6 changed files with 164 additions and 2 deletions

View file

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

View file

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

View file

@ -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 ""

View file

@ -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]

View file

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

View file

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