From c28b4dc37b289b05ba16e77602dee65ad71c1cdf Mon Sep 17 00:00:00 2001 From: luxferre Date: Mon, 22 Jun 2026 13:45:37 +0100 Subject: [PATCH] feat: applied model mixins IdMixin used on every table with an ID index (no changes needed to db) Timestamp and Deleted mixins applied to org and user tables. ActivatedMixin added to users. --- alembic/versions/2026-06-22_model_mixins.py | 44 +++++++++++++++++++++ src/contact/models.py | 5 ++- src/iam/models.py | 9 ++--- src/organisation/models.py | 7 ++-- src/service/models.py | 5 +-- src/user/models.py | 5 ++- 6 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 alembic/versions/2026-06-22_model_mixins.py diff --git a/alembic/versions/2026-06-22_model_mixins.py b/alembic/versions/2026-06-22_model_mixins.py new file mode 100644 index 0000000..db030e1 --- /dev/null +++ b/alembic/versions/2026-06-22_model_mixins.py @@ -0,0 +1,44 @@ +"""model mixins + +Revision ID: 661202797ecd +Revises: 869d48618a1c +Create Date: 2026-06-22 13:29:39.689067 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '661202797ecd' +down_revision: Union[str, Sequence[str], None] = '869d48618a1c' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('organisation', sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) + op.add_column('organisation', sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) + op.add_column('organisation', sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True)) + op.add_column('user', sa.Column('active', sa.Boolean(), nullable=False, server_default=sa.false())) + op.add_column('user', sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) + op.add_column('user', sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) + op.add_column('user', sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('user', 'deleted_at') + op.drop_column('user', 'updated_at') + op.drop_column('user', 'created_at') + op.drop_column('user', 'active') + op.drop_column('organisation', 'deleted_at') + op.drop_column('organisation', 'updated_at') + op.drop_column('organisation', 'created_at') + # ### end Alembic commands ### diff --git a/src/contact/models.py b/src/contact/models.py index ca359cd..37665e5 100644 --- a/src/contact/models.py +++ b/src/contact/models.py @@ -6,16 +6,17 @@ Models: street_address, street_address_line_2, post_office_box_number, address_locality, country_code, address_region, postal_code """ +from src.models import IdMixin + from sqlalchemy import ForeignKey from sqlalchemy.orm import mapped_column, Mapped from src.models import CustomBase -class Contact(CustomBase): +class Contact(CustomBase, IdMixin): __tablename__ = "contact" - id: Mapped[int] = mapped_column(primary_key=True) email: Mapped[str] = mapped_column(default=None, nullable=True) first_name: Mapped[str] = mapped_column(default=None, nullable=True) last_name: Mapped[str] = mapped_column(default=None, nullable=True) diff --git a/src/iam/models.py b/src/iam/models.py index 1f6d9ba..7fdb7c7 100644 --- a/src/iam/models.py +++ b/src/iam/models.py @@ -21,13 +21,12 @@ Models: from sqlalchemy import ForeignKey, UniqueConstraint from sqlalchemy.orm import relationship, mapped_column, Mapped -from src.models import CustomBase +from src.models import CustomBase, IdMixin -class Permission(CustomBase): +class Permission(CustomBase, IdMixin): __tablename__ = "permission" - id: Mapped[int] = mapped_column(primary_key=True) resource: Mapped[str] action: Mapped[str] @@ -61,9 +60,9 @@ class Permission(CustomBase): return self.service_rel.name -class Group(CustomBase): +class Group(CustomBase, IdMixin): __tablename__ = "group" - id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] org_id: Mapped[int] = mapped_column(ForeignKey("organisation.id", ondelete="CASCADE")) diff --git a/src/organisation/models.py b/src/organisation/models.py index e6f5acf..97d1247 100644 --- a/src/organisation/models.py +++ b/src/organisation/models.py @@ -14,18 +14,19 @@ Models: - OrgUsers: org_id[FK][PK], user_id[FK][PK] """ +from src.models import IdMixin, DeletedTimestampMixin + from typing import Any from sqlalchemy import ForeignKey from sqlalchemy.orm import relationship, Mapped, mapped_column -from src.models import CustomBase +from src.models import CustomBase, TimestampMixin -class Organisation(CustomBase): +class Organisation(CustomBase, IdMixin, TimestampMixin, DeletedTimestampMixin): __tablename__ = "organisation" - id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] status: Mapped[str] = mapped_column(default="partial") intake_questionnaire: Mapped[dict[str, Any] | None] diff --git a/src/service/models.py b/src/service/models.py index 63719a6..de6dcd6 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -8,13 +8,12 @@ Models: from sqlalchemy.orm import relationship, mapped_column, Mapped -from src.models import CustomBase +from src.models import CustomBase, IdMixin -class Service(CustomBase): +class Service(CustomBase, IdMixin): __tablename__ = "service" - id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(unique=True) api_key: Mapped[str] diff --git a/src/user/models.py b/src/user/models.py index 5f603ee..4803d54 100644 --- a/src/user/models.py +++ b/src/user/models.py @@ -10,6 +10,8 @@ Models: - groups: Calc property dict of {group_rel.org_rel.name: group_rel.name} """ +from src.models import IdMixin, ActivatedMixin, TimestampMixin, DeletedTimestampMixin + from collections import defaultdict from sqlalchemy.orm import relationship, mapped_column, Mapped @@ -17,10 +19,9 @@ from sqlalchemy.orm import relationship, mapped_column, Mapped from src.models import CustomBase -class User(CustomBase): +class User(CustomBase, IdMixin, ActivatedMixin, TimestampMixin, DeletedTimestampMixin): __tablename__ = "user" - id: Mapped[int] = mapped_column(primary_key=True) email: Mapped[str] first_name: Mapped[str] last_name: Mapped[str]