11import typing as t
22
33import sqlalchemy as sa
4+ import sqlalchemy .orm as sa_orm
5+ from pydantic .v1 import BaseModel
46
57from ellar_sql .constant import ABSTRACT_KEY , DATABASE_KEY , DEFAULT_KEY , TABLE_KEY
8+ from ellar_sql .model .utils import (
9+ camel_to_snake_case ,
10+ make_metadata ,
11+ should_set_table_name ,
12+ )
13+ from ellar_sql .schemas import ModelBaseConfig , ModelMetaStore
614
7- from .utils import camel_to_snake_case , make_metadata , should_set_table_name
15+
16+ class asss (BaseModel ):
17+ sd : str
18+
19+
20+ IncEx = t .Union [t .Set [int ], t .Set [str ], t .Dict [int , t .Any ], t .Dict [str , t .Any ]]
821
922if t .TYPE_CHECKING :
1023 from .base import ModelBase
@@ -30,33 +43,48 @@ def __init_subclass__(cls, **kwargs: t.Dict[str, t.Any]) -> None:
3043
3144class DatabaseBindKeyMixin :
3245 metadata : sa .MetaData
33- __dnd__ = "Ellar"
3446
3547 def __init_subclass__ (cls , ** kwargs : t .Dict [str , t .Any ]) -> None :
3648 if not ("metadata" in cls .__dict__ or TABLE_KEY in cls .__dict__ ) and hasattr (
3749 cls , DATABASE_KEY
3850 ):
3951 database_bind_key = getattr (cls , DATABASE_KEY , DEFAULT_KEY )
4052 parent_metadata = getattr (cls , "metadata" , None )
41- metadata = make_metadata (database_bind_key )
53+ db_metadata = make_metadata (database_bind_key )
4254
43- if metadata is not parent_metadata :
44- cls .metadata = metadata
55+ if db_metadata .metadata is not parent_metadata :
56+ cls .metadata = db_metadata .metadata
57+ cls .registry = db_metadata .registry # type:ignore[attr-defined]
4558
4659 super ().__init_subclass__ (** kwargs )
4760
4861
4962class ModelTrackMixin :
5063 metadata : sa .MetaData
64+ __mms__ : ModelMetaStore
65+ __table__ : sa .Table
5166
5267 def __init_subclass__ (cls , ** kwargs : t .Dict [str , t .Any ]) -> None :
68+ options : ModelBaseConfig = kwargs .pop ( # type:ignore[assignment]
69+ "options" ,
70+ ModelBaseConfig (as_base = False , use_bases = [sa_orm .DeclarativeBase ]),
71+ )
72+
5373 super ().__init_subclass__ (** kwargs )
5474
5575 if TABLE_KEY in cls .__dict__ and ABSTRACT_KEY not in cls .__dict__ :
5676 __ellar_sqlalchemy_models__ [str (cls )] = cls # type:ignore[assignment]
5777
78+ cls .__mms__ = ModelMetaStore (
79+ base_config = options ,
80+ pk_column = None ,
81+ columns = list (cls .__table__ .columns ), # type:ignore[arg-type]
82+ )
83+
5884
5985class ModelDataExportMixin :
86+ __mms__ : t .Optional [ModelMetaStore ] = None
87+
6088 def __repr__ (self ) -> str :
6189 state = sa .inspect (self )
6290 assert state is not None
@@ -70,13 +98,46 @@ def __repr__(self) -> str:
7098
7199 return f"<{ type (self ).__name__ } { pk } >"
72100
73- def dict (self , exclude : t .Optional [t .Set [str ]] = None ) -> t .Dict [str , t .Any ]:
101+ def _calculate_keys (
102+ self ,
103+ include : t .Optional [t .Set [str ]],
104+ exclude : t .Optional [t .Set [str ]],
105+ ) -> t .Set [str ]:
106+ keys : t .Set [str ] = {k for k in self .__dict__ .keys () if not k .startswith ("_sa" )}
107+
108+ if include is None and exclude is None :
109+ return keys
110+
111+ if include is not None :
112+ keys &= include
113+
114+ if exclude :
115+ keys -= exclude
116+
117+ return keys
118+
119+ def _iter (
120+ self ,
121+ include : t .Optional [t .Set [str ]],
122+ exclude : t .Optional [t .Set [str ]],
123+ exclude_none : bool = False ,
124+ ) -> t .Generator [t .Tuple [str , t .Any ], None , None ]:
125+ allowed_keys = self ._calculate_keys (include = include , exclude = exclude )
126+
127+ for field_key , v in self .__dict__ .items ():
128+ if (allowed_keys is not None and field_key not in allowed_keys ) or (
129+ exclude_none and v is None
130+ ):
131+ continue
132+ yield field_key , v
133+
134+ def dict (
135+ self ,
136+ include : t .Optional [t .Set [str ]] = None ,
137+ exclude : t .Optional [t .Set [str ]] = None ,
138+ exclude_none : bool = False ,
139+ ) -> t .Dict [str , t .Any ]:
74140 # TODO: implement advance exclude and include that goes deep into relationships too
75- _exclude : t .Set [str ] = set () if not exclude else exclude
76-
77- tuple_generator = (
78- (k , v )
79- for k , v in self .__dict__ .items ()
80- if k not in _exclude and not k .startswith ("_sa" )
141+ return dict (
142+ self ._iter (include = include , exclude_none = exclude_none , exclude = exclude )
81143 )
82- return dict (tuple_generator )
0 commit comments