Skip to content

Commit 155f943

Browse files
committed
fix: Improve the serialize_collection performance
1 parent 4c25e7d commit 155f943

1 file changed

Lines changed: 73 additions & 15 deletions

File tree

sqlalchemy_serializer/serializer.py

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,28 @@ def get_tzinfo(self):
7373
"""
7474
return
7575

76+
def _get_serializer_options(self, **kwargs) -> dict:
77+
"""Extract serializer options from kwargs, using model instance defaults as fallback.
78+
79+
:param kwargs: Options to override model defaults
80+
:return: Dictionary of options for Serializer initialization
81+
"""
82+
return {
83+
"date_format": kwargs.get("date_format") or self.date_format,
84+
"datetime_format": kwargs.get("datetime_format") or self.datetime_format,
85+
"time_format": kwargs.get("time_format") or self.time_format,
86+
"decimal_format": kwargs.get("decimal_format") or self.decimal_format,
87+
"tzinfo": kwargs.get("tzinfo") or self.get_tzinfo(),
88+
"serialize_types": kwargs.get("serialize_types") or self.serialize_types,
89+
"exclude_values": kwargs.get("exclude_values") or self.exclude_values,
90+
"max_serialization_depth": (
91+
kwargs.get("max_serialization_depth")
92+
if kwargs.get("max_serialization_depth") is not None
93+
else self.max_serialization_depth
94+
),
95+
"serialize_columns": kwargs.get("serialize_columns") or self.serialize_columns,
96+
}
97+
7698
def to_dict(
7799
self,
78100
only=(),
@@ -108,21 +130,18 @@ def to_dict(
108130
Custom serializers replace normal serialization for matching columns.
109131
:return: data: dict
110132
"""
111-
s = Serializer(
112-
date_format=date_format or self.date_format,
113-
datetime_format=datetime_format or self.datetime_format,
114-
time_format=time_format or self.time_format,
115-
decimal_format=decimal_format or self.decimal_format,
116-
tzinfo=tzinfo or self.get_tzinfo(),
117-
serialize_types=serialize_types or self.serialize_types,
118-
exclude_values=exclude_values or self.exclude_values,
119-
max_serialization_depth=(
120-
max_serialization_depth
121-
if max_serialization_depth is not None
122-
else self.max_serialization_depth
123-
),
124-
serialize_columns=serialize_columns or self.serialize_columns,
133+
options = self._get_serializer_options(
134+
date_format=date_format,
135+
datetime_format=datetime_format,
136+
time_format=time_format,
137+
decimal_format=decimal_format,
138+
tzinfo=tzinfo,
139+
serialize_types=serialize_types,
140+
exclude_values=exclude_values,
141+
max_serialization_depth=max_serialization_depth,
142+
serialize_columns=serialize_columns,
125143
)
144+
s = Serializer(**options)
126145
return s(self, only=only, extend=rules) # type: ignore
127146

128147

@@ -378,4 +397,43 @@ def get_type(value) -> str:
378397

379398

380399
def serialize_collection(iterable: t.Iterable, *args, **kwargs) -> list:
381-
return [item.to_dict(*args, **kwargs) for item in iterable]
400+
"""Serialize a collection of model instances efficiently by reusing a single Serializer.
401+
402+
This optimized version creates a single Serializer instance and reuses it for all items,
403+
eliminating redundant object creation overhead for large collections.
404+
"""
405+
iterable = list(iterable) # Convert to list to allow multiple passes
406+
if not iterable:
407+
return []
408+
409+
# Get options from first item to create a shared Serializer
410+
first_item = iterable[0]
411+
options = first_item._get_serializer_options(**kwargs)
412+
413+
# Extract only and rules from kwargs or positional args
414+
# to_dict accepts only and rules as positional args, so handle both cases
415+
only = kwargs.get("only", args[0] if len(args) > 0 else ())
416+
rules = kwargs.get("rules", args[1] if len(args) > 1 else ())
417+
418+
# Create a single Serializer instance to reuse for all items
419+
serializer = Serializer(**options)
420+
421+
# Serialize each item using the shared Serializer
422+
result = []
423+
for item in iterable:
424+
# Create a fresh schema for this item
425+
serializer.schema = Schema()
426+
427+
# Update schema with provided only/rules first (simulating __call__ behavior)
428+
# This matches the behavior in Serializer.__call__ where schema is updated
429+
# before serialize is called
430+
serializer.schema.update(only=only, extend=rules)
431+
432+
# Serialize the item - serialize_model will update the schema again
433+
# with item.serialize_only and item.serialize_rules, which matches
434+
# the original behavior where __call__ updates schema, then serialize_model
435+
# updates it again
436+
serialized = serializer.serialize(item)
437+
result.append(serialized)
438+
439+
return result

0 commit comments

Comments
 (0)