Skip to content

Commit a4d5a35

Browse files
committed
reimplemented depth logic to abstract serializer
1 parent 753cd21 commit a4d5a35

File tree

1 file changed

+29
-56
lines changed

1 file changed

+29
-56
lines changed

api_v2/serializers/abstracts.py

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33

44
from api_v2 import models
55

6-
76
class GameContentSerializer(serializers.HyperlinkedModelSerializer):
87

98
def remove_unwanted_fields(self, dynamic_params):
109
"""
11-
Takes the value of the 'fields', a string of comma-seperated values,
10+
Takes the value of the 'fields', a string of comma-separated values,
1211
and removes all other fields from the serializer
1312
"""
1413
if fields_to_keep := dynamic_params.pop("fields", None):
@@ -28,24 +27,12 @@ def get_or_create_dynamic_params(self, child):
2827

2928
@staticmethod
3029
def split_param(dynamic_param):
31-
"""
32-
Splits a dynamic parameter into a key value pair. The key is used to
33-
index into the serializers context to pass nested parameters
34-
35-
document__fields=name -> ['document', 'fields=name']
36-
document__licenses__fields=name -> ['document', 'licenses__fields=name']
37-
38-
'__'s after the first are preserved so that the dynamic parameter can
39-
be passed recursively down to more deeply-nested serializers
40-
"""
4130
crumbs = dynamic_param.split("__")
4231
return crumbs[0], "__".join(crumbs[1:]) if len(crumbs) > 1 else None
4332

4433
def set_dynamic_params_for_children(self, dynamic_params):
4534
"""
46-
Passes nested dynamic params to child serializer. ie.
47-
"document__fields=name" would pass the query param "fields=name" to the
48-
nested Document Serializer via the 'context' prop
35+
Passes nested dynamic params to child serializer.
4936
"""
5037
for param, fields in dynamic_params.items():
5138
child, child_dynamic_param = self.split_param(param)
@@ -55,68 +42,54 @@ def set_dynamic_params_for_children(self, dynamic_params):
5542

5643
@staticmethod
5744
def is_param_dynamic(p):
58-
"""
59-
Only the 'fields' query param supported, so we test for that
60-
"""
6145
return p.endswith("fields")
6246

6347
def get_dynamic_params_for_root(self, request):
64-
"""
65-
Checks query params in request and returns dynamic parameters
66-
"""
6748
query_params = request.query_params.items()
6849
return {k: v for k, v in query_params if self.is_param_dynamic(k)}
6950

7051
def get_dynamic_params(self):
71-
"""
72-
When dynamic params get passed down in set_context_for_children
73-
If the child is a subclass of ListSerializer (has many=True)
74-
The context must be fetched from ListSerializer Class
75-
"""
7652
if isinstance(self.parent, serializers.ListSerializer):
7753
return self.parent._context.get("dynamic_params", {})
7854
return self._context.get("dynamic_params", {})
7955

80-
def set_depth(self, depth):
81-
"""
82-
Add appropriate 'depth' param to self.Meta based on 'depth' query param
83-
"""
84-
if not depth:
85-
self.Meta.depth = 0
86-
return
87-
try:
88-
depth = int(depth)
89-
if depth > 0 and depth < 3:
90-
# This value going above 1 could cause performance issues.
91-
# Limited to 1 and 2 for now.
92-
self.Meta.depth = depth
93-
# Depth does not reset by default on subsequent requests with malformed urls.
94-
else:
95-
self.Meta.depth = 0
96-
except ValueError:
97-
pass # it was not castable to an int.
98-
99-
10056
def __init__(self, *args, **kwargs):
10157
request = kwargs.get("context", {}).get("request")
10258
super().__init__(*args, **kwargs)
10359

104-
105-
# "request" doesn't exist on the child serializers, or when generating OAS spec
106-
# So this if only evaluates as true on the root serializer
107-
108-
is_root = bool(request)
109-
if is_root:
110-
self.set_depth(request.query_params.get('depth'))
60+
if request:
61+
self._context["max_depth"] = int(request.query_params.get("depth", 0))
11162
dynamic_params = self.get_dynamic_params_for_root(request)
11263
self._context.update({"dynamic_params": dynamic_params})
113-
114-
def to_representation(self, *args, **kwargs):
64+
65+
def to_representation(self, instance):
66+
max_depth = self._context.get("max_depth", 0)
67+
current_depth = self._context.get("current_depth", 0)
68+
69+
# Process dynamic parameters for filtering fields
11570
if dynamic_params := self.get_dynamic_params().copy():
11671
self.remove_unwanted_fields(dynamic_params)
11772
self.set_dynamic_params_for_children(dynamic_params)
11873

119-
return super().to_representation(*args, **kwargs)
74+
# Collect only the fields that need to be included in the representation
75+
representation = super().to_representation(instance)
76+
77+
if current_depth >= max_depth:
78+
# Remove fields that are HyperlinkedModelSerializers (nested fields)
79+
for field_name, field in self.fields.items():
80+
if isinstance(field, serializers.HyperlinkedModelSerializer):
81+
# Check if the nested field has a 'url' attribute in the representation
82+
nested_representation = representation.get(field_name)
83+
if nested_representation and "url" in nested_representation:
84+
# Replace the entire nested structure with the URL field
85+
representation[field_name] = nested_representation["url"]
86+
else:
87+
# Update depth level in children
88+
for field_name, field in self.fields.items():
89+
if isinstance(field, GameContentSerializer):
90+
field._context["current_depth"] = current_depth + 1
91+
92+
return representation
12093

12194
class Meta:
12295
abstract = True

0 commit comments

Comments
 (0)