10
10
asdict ,
11
11
dataclass ,
12
12
is_dataclass ,
13
- make_dataclass ,
14
- fields
13
+ make_dataclass
15
14
)
16
15
from typing import Any , Optional , Union
17
16
from functools import partial
18
17
from enum import EnumMeta
19
18
from operator import attrgetter
20
19
from orjson import OPT_INDENT_2
21
20
from datamodel .converters import parse_type
22
- from datamodel .fields import Field
21
+ from datamodel .fields import Field , fields
23
22
from datamodel .types import JSON_TYPES
24
23
from datamodel .validation import validator
25
24
@@ -86,13 +85,14 @@ def _dc_method_setattr(
86
85
87
86
def create_dataclass (
88
87
new_cls : Union [object , Any ],
88
+ strict : bool = False ,
89
89
frozen : bool = False
90
90
) -> Callable :
91
91
"""
92
92
create_dataclass.
93
93
Create a Dataclass from a simple Class
94
94
"""
95
- dc = dataclass (unsafe_hash = True , repr = False , init = True , order = False , eq = True , frozen = frozen )(new_cls )
95
+ dc = dataclass (unsafe_hash = strict , repr = False , init = True , order = False , eq = True , frozen = frozen )(new_cls )
96
96
setattr (dc , "__setattr__" , _dc_method_setattr )
97
97
# adding a properly internal json encoder:
98
98
dc .__encoder__ = DefaultEncoder ()
@@ -113,9 +113,15 @@ def __new__(cls, name, bases, attrs, **kwargs):
113
113
"""__new__ is a classmethod, even without @classmethod decorator"""
114
114
cols = []
115
115
if "__annotations__" in attrs :
116
- annotations = attrs ["__annotations__" ]
116
+ annotations = attrs .get ('__annotations__' , {})
117
+ try :
118
+ strict = attrs ['Meta' ].strict
119
+ except (TypeError , AttributeError , KeyError ):
120
+ strict = True
117
121
for field , _type in annotations .items ():
118
- if field in attrs :
122
+ if strict is False and field not in attrs :
123
+ attrs [field ] = Field (factory = _type , required = False , default = None )
124
+ elif field in attrs :
119
125
df = attrs [field ]
120
126
if isinstance (df , Field ):
121
127
setattr (cls , field , df )
@@ -125,7 +131,6 @@ def __new__(cls, name, bases, attrs, **kwargs):
125
131
df .type = _type
126
132
setattr (cls , field , df )
127
133
else :
128
- # print(f"HERE Field: {field}, Type: {_type}")
129
134
# add a new field, based on type
130
135
df = Field (factory = _type , required = False , default = None )
131
136
df .name = field
@@ -137,6 +142,7 @@ def __new__(cls, name, bases, attrs, **kwargs):
137
142
attr_meta = attrs .pop ("Meta" , None )
138
143
new_cls = super ().__new__ (cls , name , bases , attrs , ** kwargs )
139
144
new_cls .Meta = attr_meta or getattr (new_cls , "Meta" , Meta )
145
+ new_cls .__dataclass_fields__ = cols
140
146
if not new_cls .Meta :
141
147
new_cls .Meta = Meta
142
148
new_cls .Meta .set_connection = types .MethodType (
@@ -166,6 +172,7 @@ def __new__(cls, name, bases, attrs, **kwargs):
166
172
pass
167
173
dc = create_dataclass (
168
174
new_cls ,
175
+ strict = new_cls .Meta .strict ,
169
176
frozen = frozen
170
177
)
171
178
cols = {
@@ -186,7 +193,7 @@ def __init__(cls, *args, **kwargs) -> None:
186
193
# Initialized Data Model = True
187
194
cls .__initialised__ = True
188
195
cls .__errors__ = None
189
- super (ModelMeta , cls ).__init__ (* args , ** kwargs )
196
+ super ().__init__ (* args , ** kwargs )
190
197
191
198
192
199
class BaseModel (metaclass = ModelMeta ):
@@ -247,6 +254,20 @@ def json(self, **kwargs):
247
254
def is_valid (self ):
248
255
return bool (self .__valid__ )
249
256
257
+ @classmethod
258
+ def add_field (cls , name : str , value : Any = None ) -> None :
259
+ if cls .Meta .strict is True :
260
+ raise TypeError (
261
+ f'Cannot create a new field { name } on a Strict Model.'
262
+ )
263
+ if name != '__errors__' :
264
+ f = Field (required = False , default = value )
265
+ f .name = name
266
+ f .type = type (value )
267
+ f ._field_type = _FIELD
268
+ cls .__columns__ [name ] = f
269
+ cls .__dataclass_fields__ [name ] = f
270
+
250
271
def create_field (self , name : str , value : Any ) -> None :
251
272
"""create_field.
252
273
create a new Field on Model (when strict is False).
@@ -264,7 +285,9 @@ def create_field(self, name: str, value: Any) -> None:
264
285
f = Field (required = False , default = value )
265
286
f .name = name
266
287
f .type = type (value )
288
+ f ._field_type = _FIELD
267
289
self .__columns__ [name ] = f
290
+ self .__dataclass_fields__ [name ] = f
268
291
setattr (self , name , value )
269
292
270
293
def set (self , name : str , value : Any ) -> None :
@@ -356,14 +379,6 @@ def __post_init__(self) -> None:
356
379
self ._validation ()
357
380
except RuntimeError as err :
358
381
logging .exception (err )
359
- ### Post Init
360
- ## check if remove nulls:
361
- # print('META >>> ', self.Meta.nulls)
362
- # if self.Meta.remove_nulls is True:
363
- # null_fields = [field_name for field_name, value in self.__dict__.items() if value is None]
364
- # print('NULL FIELDS >> ', null_fields)
365
- # for field_name in null_fields:
366
- # delattr(self, field_name)
367
382
368
383
369
384
def is_callable (self , value ) -> bool :
0 commit comments