1313# See the License for the specific language governing permissions and
1414# limitations under the License.
1515# ===============================================================================
16+ """
17+ db/base.py
18+
19+ This file defines the foundational components for the SQLAlchemy models.
20+ It includes:
21+ 1. The declarative base class (`Base`) that all models will inherit from.
22+ 2. A helper function (`lexicon_term`) to create standardized foreign key columns
23+ referencing the `lexicon_term` table.
24+ 3. A helper function (`pascal_to_snake`) to convert class names from PascalCase to snake_case
25+ for automatic table naming.
26+ 4. Mixins for common functionality:
27+ - `AutoBaseMixin`: Adds automatic table naming and an auto-incrementing primary key.
28+ - `PropertiesMixin`: Adds a JSONB properties column for storing additional attributes.
29+ - `ReleaseMixin`: Adds a release status column referencing the `lexicon_term` table.
30+ - `AuditMixin`: Adds standard audit columns (created_at, created_by, updated_at, updated_by).
31+ 5. A simple `User` model for tracking user information in audit columns.
32+ 6. Polymorphic helper mixins (`StatusHistoryMixin`, `NotesMixin`, `AttributionMixin`, etc.)
33+ which provide a clean, reusable way to add relationships to the polymorphic
34+ metadata tables. Any model that can have a status history (like Thing or Location)
35+ can simply inherit from the `StatusHistoryMixin` mixin.
36+ 7. An `AuditMixin` to add standard audit columns to tables.
37+ """
38+
1639from sqlalchemy import (
1740 Column ,
1841 DateTime ,
1942 func ,
2043 Integer ,
2144 JSON ,
2245 String ,
23- Boolean ,
24- Text ,
2546 ForeignKey ,
2647)
27- from sqlalchemy .orm import DeclarativeBase , declared_attr , Mapped , mapped_column
48+ from sqlalchemy .orm import (
49+ DeclarativeBase ,
50+ declared_attr ,
51+ Mapped ,
52+ mapped_column ,
53+ relationship ,
54+ )
2855from sqlalchemy_searchable import make_searchable
2956from sqlalchemy_continuum import make_versioned
3057import re
@@ -41,6 +68,12 @@ class Base(DeclarativeBase):
4168
4269
4370def lexicon_term (foreignkeykw = None , ** kw ):
71+ """Create a SQLAlchemy mapped column for a self-referencing lexicon term.
72+
73+ This helper function simplifies the creation of a string column that also
74+ acts as a foreign key to the 'term' column of the 'lexicon_term' table.
75+ It standardizes the column type to String(100) and sets the onupdate
76+ behavior to "CASCADE"."""
4477
4578 fkw = foreignkeykw if foreignkeykw else {}
4679
@@ -55,13 +88,18 @@ def pascal_to_snake(name):
5588 return re .sub (r"(?<!^)(?=[A-Z])" , "_" , name ).lower ()
5689
5790
91+ # ============= Common Mixins =============================================
5892class ReleaseMixin :
93+ """Mixin to add release status to a model."""
94+
5995 @declared_attr
6096 def release_status (self ):
6197 return lexicon_term (default = "draft" )
6298
6399
64100class AuditMixin :
101+ """Mixin to add standard audit columns to a model."""
102+
65103 @declared_attr
66104 def created_at (self ):
67105 return Column (
@@ -109,6 +147,8 @@ def updated_by_id(self):
109147
110148
111149class AutoBaseMixin (AuditMixin ):
150+ """Mixin to add automatic table naming and an auto-incrementing primary key."""
151+
112152 @declared_attr
113153 def __tablename__ (self ):
114154 return pascal_to_snake (self .__name__ )
@@ -119,6 +159,8 @@ def id(self):
119159
120160
121161class PropertiesMixin :
162+ """Mixin to add a JSONB properties column for storing additional attributes."""
163+
122164 @declared_attr
123165 def properties (self ):
124166 return Column (
@@ -129,7 +171,29 @@ def properties(self):
129171 )
130172
131173
174+ # ============= Polymorphic Helper Mixins =============================================
175+ class StatusHistoryMixin :
176+ """
177+ Mixin for models that can have a status history (e.g., Thing, Location).
178+ It automatically creates a polymorphic One-to-Many relationship to the
179+ StatusHistory table.
180+ """
181+
182+ @declared_attr
183+ def status_history (self ):
184+ # One-to-Many polymorphic relationship
185+ return relationship (
186+ "StatusHistory" ,
187+ primaryjoin = f"and_({ self .__name__ } .{ self .__name__ .lower ()} _id==StatusHistory.statusable_id, "
188+ f"StatusHistory.statusable_type=='{ self .__name__ } ')" ,
189+ cascade = "all, delete-orphan" ,
190+ lazy = "selectin" ,
191+ )
192+
193+
132194class User (Base ):
195+ """Represents a user in the system."""
196+
133197 __tablename__ = "user"
134198
135199 id : Mapped [int ] = mapped_column (Integer , primary_key = True , nullable = False )
0 commit comments