Skip to content

Commit 2f0caeb

Browse files
committed
implemented basic filtering via 'fields' query param
1 parent f3108e9 commit 2f0caeb

File tree

13 files changed

+104
-31
lines changed

13 files changed

+104
-31
lines changed

api_v2/serializers/abstracts.py

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,72 @@
44
from api_v2 import models
55

66

7-
class GameContentSerializer(serializers.HyperlinkedModelSerializer):
7+
class GameContentSerializer(serializers.HyperlinkedModelSerializer):
88

9-
def remove_unwanted_fields(self, fields_to_keep):
10-
fields_to_keep = set(fields_to_keep.split(","))
11-
all_fields = set(self.fields.keys())
12-
for field in all_fields - fields_to_keep:
13-
self.fields.pop(field, None)
14-
9+
def remove_unwanted_fields(self, dynamic_params):
10+
"""
11+
Takes the value of the 'fields', a string of comma-seperated values,
12+
and removes all other fields from the serializer
13+
"""
14+
if fields_to_keep := dynamic_params.pop("fields", None):
15+
fields_to_keep = set(fields_to_keep.split(","))
16+
all_fields = set(self.fields.keys())
17+
for field in all_fields - fields_to_keep:
18+
self.fields.pop(field, None)
1519

16-
# Adding dynamic "fields" qs parameter.
17-
def __init__(self, *args, **kwargs):
20+
def get_or_create_dynamic_params(self, child):
21+
"""
22+
Creates dynamic params on the serializer context if it doesn't already
23+
exist, then returns the dynamic parameters
24+
"""
25+
if "dynamic_params" not in self.fields[child]._context:
26+
self.fields[child]._context.update({"dynamic_params": {}})
27+
return self.fields[child]._context["dynamic_params"]
28+
29+
@staticmethod
30+
def split_param(dynamic_param):
31+
crumbs = dynamic_param.split("__")
32+
return crumbs[0], "__".join(crumbs[1:]) if len(crumbs) > 1 else None
33+
34+
def set_dynamic_params_for_children(self, dynamic_params):
35+
for param, fields in dynamic_params.items():
36+
child, child_dynamic_param = self.split_param(param)
37+
if child in set(self.fields.keys()):
38+
dynamic_params = self.get_or_create_dynamic_params(child)
39+
dynamic_params.update({child_dynamic_param: fields})
40+
41+
@staticmethod
42+
def is_param_dynamic(p):
43+
"""
44+
Currently only the 'fields' query param is supported
45+
"""
46+
return p.endswith("fields")
47+
48+
def get_dynamic_params_for_root(self, request):
49+
query_params = request.query_params.items()
50+
return {k: v for k, v in query_params if self.is_param_dynamic(k)}
1851

52+
def get_dynamic_params(self):
53+
"""
54+
When dynamic params get passed down in set_context_for_children
55+
If the child is a subclass of ListSerializer (has many=True)
56+
The context must be fetched from ListSerializer Class
57+
"""
58+
if isinstance(self.parent, serializers.ListSerializer):
59+
return self.parent._context.get("dynamic_params", {})
60+
return self._context.get("dynamic_params", {})
61+
62+
def __init__(self, *args, **kwargs):
1963
request = kwargs.get("context", {}).get("request")
20-
# Instantiate the superclass normally
21-
super(GameContentSerializer, self).__init__(*args, **kwargs)
64+
super().__init__(*args, **kwargs)
2265

23-
# request only exists on root, not on nested queries or when generating OAS file
66+
# "request" doesn't exist on the child serializers, or when generating OAS spec
2467
is_root = bool(request)
25-
2668
if is_root:
27-
fields = request.query_params.get('fields')
28-
if fields:
29-
self.remove_unwanted_fields(fields)
69+
if request.method != "GET":
70+
return
3071

31-
depth = request.query_params.get('depth')
32-
if depth:
72+
if depth:= request.query_params.get('depth'):
3373
try:
3474
depth_value = int(depth)
3575
if depth_value > 0 and depth_value < 3:
@@ -44,5 +84,16 @@ def __init__(self, *args, **kwargs):
4484
else:
4585
self.Meta.depth = 0 #The default.
4686

87+
#
88+
dynamic_params = self.get_dynamic_params_for_root(request)
89+
self._context.update({"dynamic_params": dynamic_params})
90+
91+
def to_representation(self, *args, **kwargs):
92+
if dynamic_params := self.get_dynamic_params().copy():
93+
self.remove_unwanted_fields(dynamic_params)
94+
self.set_dynamic_params_for_children(dynamic_params)
95+
96+
return super().to_representation(*args, **kwargs)
97+
4798
class Meta:
48-
abstract = True
99+
abstract = True

api_v2/serializers/alignment.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

910
class AlignmentSerializer(GameContentSerializer):
1011
key = serializers.ReadOnlyField()
1112
morality = serializers.ReadOnlyField()
1213
societal_attitude = serializers.ReadOnlyField()
1314
short_name = serializers.ReadOnlyField()
14-
15+
document = DocumentSerializer()
16+
1517
class Meta:
1618
model = models.Alignment
1719
fields = '__all__'

api_v2/serializers/background.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

910
class BackgroundBenefitSerializer(serializers.ModelSerializer):
1011
class Meta:
@@ -17,6 +18,7 @@ class BackgroundSerializer(GameContentSerializer):
1718
benefits = BackgroundBenefitSerializer(
1819
many=True
1920
)
21+
document = DocumentSerializer()
2022

2123
class Meta:
2224
model = models.Background

api_v2/serializers/characterclass.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

9-
class ClassFeatureItemSerializer(serializers.ModelSerializer):
10+
class ClassFeatureItemSerializer(GameContentSerializer):
1011
class Meta:
1112
model = models.ClassFeatureItem
1213
fields = ['name','desc','type']
1314

14-
class ClassFeatureSerializer(serializers.ModelSerializer):
15+
class ClassFeatureSerializer(GameContentSerializer):
1516
key = serializers.ReadOnlyField()
1617

1718
class Meta:
@@ -24,6 +25,7 @@ class CharacterClassSerializer(GameContentSerializer):
2425
many=True, context={'request': {}})
2526
levels = serializers.ReadOnlyField()
2627
hit_points = serializers.ReadOnlyField()
28+
document = DocumentSerializer()
2729

2830
class Meta:
2931
model = models.CharacterClass

api_v2/serializers/condition.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

910
class ConditionSerializer(GameContentSerializer):
1011
key = serializers.ReadOnlyField()
12+
document = DocumentSerializer()
1113

1214
class Meta:
1315
model = models.Condition

api_v2/serializers/creature.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from api_v2 import models
88

99
from .abstracts import GameContentSerializer
10+
from .document import DocumentSerializer
1011
from .size import SizeSerializer
1112
from drf_spectacular.utils import extend_schema_field, inline_serializer
1213
from drf_spectacular.types import OpenApiTypes
@@ -59,7 +60,7 @@ class CreatureSerializer(GameContentSerializer):
5960
speed_all = serializers.SerializerMethodField()
6061
challenge_rating_text = serializers.SerializerMethodField()
6162
experience_points = serializers.SerializerMethodField()
62-
63+
document = DocumentSerializer()
6364

6465
class Meta:
6566
'''Serializer meta options.'''

api_v2/serializers/document.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44

55
from api_v2 import models
66

7-
class GameSystemSerializer(serializers.HyperlinkedModelSerializer):
7+
class GameSystemSerializer(GameContentSerializer):
88
key = serializers.ReadOnlyField()
99

1010
class Meta:
1111
model = models.GameSystem
1212
fields = '__all__'
1313

1414

15-
class LicenseSerializer(serializers.HyperlinkedModelSerializer):
15+
class LicenseSerializer(GameContentSerializer):
1616
key = serializers.ReadOnlyField()
1717

1818
class Meta:
1919
model = models.License
2020
fields = '__all__'
2121

2222

23-
class PublisherSerializer(serializers.HyperlinkedModelSerializer):
23+
class PublisherSerializer(GameContentSerializer):
2424
key = serializers.ReadOnlyField()
2525

2626
class Meta:
@@ -30,6 +30,9 @@ class Meta:
3030

3131
class DocumentSerializer(GameContentSerializer):
3232
key = serializers.ReadOnlyField()
33+
licenses = LicenseSerializer(many=True)
34+
publisher = PublisherSerializer()
35+
gamesystem = GameSystemSerializer()
3336

3437
class Meta:
3538
model = models.Document

api_v2/serializers/feat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

910
class FeatBenefitSerializer(serializers.ModelSerializer):
1011
class Meta:
@@ -16,6 +17,7 @@ class FeatSerializer(GameContentSerializer):
1617
has_prerequisite = serializers.ReadOnlyField()
1718
benefits = FeatBenefitSerializer(
1819
many=True)
20+
document = DocumentSerializer()
1921

2022
class Meta:
2123
model = models.Feat

api_v2/serializers/item.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from .abstracts import GameContentSerializer
88
from .size import SizeSerializer
9+
from .document import DocumentSerializer
910
from drf_spectacular.utils import extend_schema_field
1011
from drf_spectacular.types import OpenApiTypes
1112

@@ -52,6 +53,7 @@ class ItemSerializer(GameContentSerializer):
5253
is_magic_item = serializers.ReadOnlyField()
5354
weapon = WeaponSerializer(read_only=True, context={'request':{}})
5455
armor = ArmorSerializer(read_only=True, context={'request':{}})
56+
document = DocumentSerializer()
5557

5658
class Meta:
5759
model = models.Item

api_v2/serializers/language.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
from api_v2 import models
66

77
from .abstracts import GameContentSerializer
8+
from .document import DocumentSerializer
89

9-
class LanguageSerializer(serializers.ModelSerializer):
10+
class LanguageSerializer(GameContentSerializer):
1011
key = serializers.ReadOnlyField()
11-
12+
document = DocumentSerializer()
1213
class Meta:
1314
model = models.Language
1415
fields = '__all__'

0 commit comments

Comments
 (0)