3333#
3434##########################################################################
3535
36- SCHEMA_VERSION = 49
36+ SCHEMA_VERSION = 50
3737
3838##########################################################################
3939#
5151SERVER_ID = 'server.id'
5252CASCADE_STR = "all, delete-orphan"
5353
54+
55+ class UserScopedMixin :
56+ """Mixin for models that store per-user data.
57+
58+ Provides for_user() as the default scoped query entry point.
59+ Models with a 'user_id' column or a 'uid' column are supported
60+ automatically — the mixin detects which column name is used.
61+
62+ Usage:
63+ # Instead of:
64+ Process.query.filter_by(user_id=current_user.id, pid=pid)
65+ # Use:
66+ Process.for_user(pid=pid)
67+ """
68+
69+ @classmethod
70+ def _user_column (cls ):
71+ """Return the user-scoping column for this model."""
72+ if hasattr (cls , 'user_id' ):
73+ return cls .user_id
74+ if hasattr (cls , 'uid' ):
75+ return cls .uid
76+ raise AttributeError (
77+ f"{ cls .__name__ } has no user_id or uid column"
78+ )
79+
80+ @classmethod
81+ def _user_column_name (cls ):
82+ """Return the column name string ('user_id' or 'uid')."""
83+ if hasattr (cls , 'user_id' ):
84+ return 'user_id'
85+ if hasattr (cls , 'uid' ):
86+ return 'uid'
87+ raise AttributeError (
88+ f"{ cls .__name__ } has no user_id or uid column"
89+ )
90+
91+ @classmethod
92+ def for_user (cls , user_id = None , ** kwargs ):
93+ """Query scoped to a specific user (defaults to current_user).
94+
95+ Args:
96+ user_id: Explicit user ID. If None, uses current_user.id.
97+ **kwargs: Additional filter_by arguments.
98+
99+ Returns:
100+ A SQLAlchemy query filtered by the user's ID.
101+ """
102+ from flask_security import current_user as cu
103+ uid = user_id if user_id is not None else cu .id
104+ kwargs [cls ._user_column_name ()] = uid
105+ return cls .query .filter_by (** kwargs )
106+
107+
54108# Define models
55109roles_users = db .Table (
56110 'roles_users' ,
@@ -158,15 +212,15 @@ class User(db.Model, UserMixin):
158212 locked = db .Column (db .Boolean (), default = False )
159213
160214
161- class Setting (db .Model ):
215+ class Setting (db .Model , UserScopedMixin ):
162216 """Define a setting object"""
163217 __tablename__ = 'setting'
164218 user_id = db .Column (db .Integer , db .ForeignKey (USER_ID ), primary_key = True )
165219 setting = db .Column (db .String (256 ), primary_key = True )
166220 value = db .Column (db .Text ())
167221
168222
169- class ServerGroup (db .Model ):
223+ class ServerGroup (db .Model , UserScopedMixin ):
170224 """Define a server group for the treeview"""
171225 __tablename__ = 'servergroup'
172226 id = db .Column (db .Integer , primary_key = True )
@@ -185,7 +239,7 @@ def serialize(self):
185239 }
186240
187241
188- class Server (db .Model ):
242+ class Server (db .Model , UserScopedMixin ):
189243 """Define a registered Postgres server"""
190244 __tablename__ = 'server'
191245 id = db .Column (db .Integer , primary_key = True )
@@ -306,7 +360,7 @@ class Preferences(db.Model):
306360 name = db .Column (db .String (1024 ), nullable = False )
307361
308362
309- class UserPreference (db .Model ):
363+ class UserPreference (db .Model , UserScopedMixin ):
310364 """Define the preference for a particular user."""
311365 __tablename__ = 'user_preferences'
312366 pid = db .Column (
@@ -318,9 +372,13 @@ class UserPreference(db.Model):
318372 value = db .Column (db .String (1024 ), nullable = False )
319373
320374
321- class DebuggerFunctionArguments (db .Model ):
375+ class DebuggerFunctionArguments (db .Model , UserScopedMixin ):
322376 """Define the debugger input function arguments."""
323377 __tablename__ = 'debugger_function_arguments'
378+ user_id = db .Column (
379+ db .Integer , db .ForeignKey (USER_ID ),
380+ nullable = False , primary_key = True
381+ )
324382 server_id = db .Column (db .Integer (), nullable = False , primary_key = True )
325383 database_id = db .Column (db .Integer (), nullable = False , primary_key = True )
326384 schema_id = db .Column (db .Integer (), nullable = False , primary_key = True )
@@ -349,7 +407,7 @@ class DebuggerFunctionArguments(db.Model):
349407 value = db .Column (db .String (), nullable = True )
350408
351409
352- class Process (db .Model ):
410+ class Process (db .Model , UserScopedMixin ):
353411 """Define the Process table."""
354412 __tablename__ = 'process'
355413 pid = db .Column (db .String (), nullable = False , primary_key = True )
@@ -382,7 +440,7 @@ class Keys(db.Model):
382440 value = db .Column (db .String (), nullable = False )
383441
384442
385- class QueryHistoryModel (db .Model ):
443+ class QueryHistoryModel (db .Model , UserScopedMixin ):
386444 """Define the history SQL table."""
387445 __tablename__ = 'query_history'
388446 srno = db .Column (db .Integer (), nullable = False , primary_key = True )
@@ -397,7 +455,7 @@ class QueryHistoryModel(db.Model):
397455 last_updated_flag = db .Column (db .String (), nullable = False )
398456
399457
400- class ApplicationState (db .Model ):
458+ class ApplicationState (db .Model , UserScopedMixin ):
401459 """Define the application state SQL table."""
402460 __tablename__ = 'application_state'
403461 uid = db .Column (db .Integer (), db .ForeignKey (USER_ID ), nullable = False ,
@@ -422,7 +480,7 @@ class Database(db.Model):
422480 )
423481
424482
425- class SharedServer (db .Model ):
483+ class SharedServer (db .Model , UserScopedMixin ):
426484 """Define a shared Postgres server"""
427485
428486 __tablename__ = 'sharedserver'
@@ -510,7 +568,7 @@ class Macros(db.Model):
510568 key_code = db .Column (db .Integer , nullable = False )
511569
512570
513- class UserMacros (db .Model ):
571+ class UserMacros (db .Model , UserScopedMixin ):
514572 """Define the macro for a particular user."""
515573 __tablename__ = 'user_macros'
516574 id = db .Column (db .Integer , primary_key = True , autoincrement = True )
@@ -524,7 +582,7 @@ class UserMacros(db.Model):
524582 sql = db .Column (db .Text (), nullable = False )
525583
526584
527- class UserMFA (db .Model ):
585+ class UserMFA (db .Model , UserScopedMixin ):
528586 """Stores the options for the MFA for a particular user."""
529587 __tablename__ = 'user_mfa'
530588 user_id = db .Column (db .Integer , db .ForeignKey (USER_ID ), primary_key = True )
0 commit comments