Skip to content

Commit 72dd64b

Browse files
Merge pull request #90 from phenobarbital/remove-nulls-from-output-and-repr
Can remove null values from Dict Output
2 parents e6d85f9 + fa8c9cf commit 72dd64b

File tree

4 files changed

+458
-254
lines changed

4 files changed

+458
-254
lines changed

datamodel/base.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
dataclass,
1212
is_dataclass,
1313
make_dataclass,
14+
fields
1415
)
1516
from typing import Any, Optional, Union
1617
from functools import partial
1718
from enum import EnumMeta
19+
from operator import attrgetter
1820
from orjson import OPT_INDENT_2
1921
from datamodel.converters import parse_type
2022
from datamodel.fields import Field
@@ -40,6 +42,7 @@ class Meta:
4042
dsn: Optional[str] = None
4143
datasource: Optional[str] = None
4244
connection: Optional[Callable] = None
45+
remove_nulls: bool = False
4346

4447
def set_connection(cls, conn: Callable):
4548
cls.connection = conn
@@ -89,7 +92,7 @@ def create_dataclass(
8992
create_dataclass.
9093
Create a Dataclass from a simple Class
9194
"""
92-
dc = dataclass(unsafe_hash=True, init=True, order=False, eq=True, frozen=frozen)(new_cls)
95+
dc = dataclass(unsafe_hash=True, repr=False, init=True, order=False, eq=True, frozen=frozen)(new_cls)
9396
setattr(dc, "__setattr__", _dc_method_setattr)
9497
# adding a properly internal json encoder:
9598
dc.__encoder__ = DefaultEncoder()
@@ -209,11 +212,31 @@ def __getitem__(self, item):
209212
def column(self, name):
210213
return self.__columns__[name]
211214

215+
def __repr__(self):
216+
nodef_f_vals = (
217+
(f.name, attrgetter(f.name)(self))
218+
for f in fields(self)
219+
if attrgetter(f.name)(self) != f.default
220+
)
221+
nodef_f_repr = ", ".join(f"{name}={value}" for name, value in nodef_f_vals)
222+
return f"{self.__class__.__name__}({nodef_f_repr})"
223+
224+
def remove_nulls(self, obj: Any) -> dict[str, Any]:
225+
"""Recursively removes any fields with None values from the given object."""
226+
if isinstance(obj, list):
227+
return [self.remove_nulls(item) for item in obj]
228+
elif isinstance(obj, dict):
229+
return {key: self.remove_nulls(value) for key, value in obj.items() if value is not None}
230+
else:
231+
return obj
232+
212233
def dict(self):
234+
if self.Meta.remove_nulls is True:
235+
return self.remove_nulls(asdict(self, dict_factory=dict))
213236
return asdict(self)
214237

215238
def to_dict(self):
216-
return asdict(self)
239+
return self.dict()
217240

218241
def json(self, **kwargs):
219242
encoder = self.__encoder__
@@ -333,6 +356,15 @@ def __post_init__(self) -> None:
333356
self._validation()
334357
except RuntimeError as err:
335358
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+
336368

337369
def is_callable(self, value) -> bool:
338370
is_missing = (value == _MISSING_TYPE)
@@ -559,11 +591,11 @@ def schema(cls, as_dict: bool = False) -> Any:
559591
"enum": list(map(lambda c: c.value, _type))
560592
}
561593
elif isinstance(_type, ModelMeta):
562-
594+
563595
t = 'object'
564596
enum_type = None
565597
sch = _type.schema(as_dict = True)
566-
598+
567599
if 'fk' in field.metadata:
568600
api = field.metadata['api'] if 'api' in field.metadata else sch['table']
569601
fk = field.metadata['fk'].split("|")
@@ -574,7 +606,7 @@ def schema(cls, as_dict: bool = False) -> Any:
574606
}
575607
else:
576608
ref = sch['$id']
577-
609+
578610
defs[name] = sch
579611
else:
580612
ref = None

0 commit comments

Comments
 (0)