Skip to content

Commit 3f82d75

Browse files
committed
raise ValidationError if sideloading request is invalid
1 parent 64384d4 commit 3f82d75

File tree

2 files changed

+60
-53
lines changed

2 files changed

+60
-53
lines changed

drf_sideloading/mixins.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from typing import Dict, Optional, Union, Tuple, Set
55

66
from django.db.models import Prefetch
7+
from django.utils.translation import gettext_lazy as _
78
from rest_framework.exceptions import ValidationError
89
from rest_framework.generics import get_object_or_404
9-
from rest_framework.renderers import BrowsableAPIRenderer
1010
from rest_framework.response import Response
1111
from rest_framework.serializers import ListSerializer
1212

@@ -36,8 +36,11 @@ def get_sideloading_variables_from_serializer(self, request):
3636
)
3737
relations_to_sideload = self.parse_query_param(
3838
sideload_parameter=request.query_params.get(self.sideloading_query_param_name, ""),
39-
sideloadable_fields=sideloadable_fields,
4039
)
40+
self.check_sideload_params(
41+
relations_to_sideload=relations_to_sideload, sideloadable_fields=sideloadable_fields, prefetches=prefetches
42+
)
43+
4144
# find applicable prefetches
4245
if relations_to_sideload:
4346
prefetch_relations, relations_sources = self._get_relevant_prefetches(
@@ -58,7 +61,7 @@ def get_sideloading_variables_from_serializer(self, request):
5861
relations_sources,
5962
)
6063

61-
def parse_query_param(self, sideload_parameter, sideloadable_fields):
64+
def parse_query_param(self, sideload_parameter: str) -> Dict:
6265
"""
6366
Parse query param and take validated names
6467
@@ -73,20 +76,37 @@ def parse_query_param(self, sideload_parameter, sideloadable_fields):
7376
response changed to dict as the sources for multi source fields must be selectable.
7477
7578
"""
76-
sideloadable_relations = set(sideloadable_fields.keys())
79+
if not sideload_parameter:
80+
return {}
7781

7882
relations_to_sideload = {}
7983
for param in re.split(",\s*(?![^\[\]]*\])", sideload_parameter):
84+
if not param:
85+
continue
8086
try:
8187
fieldname, sources_str = param.split("[", 1)
8288
relations = set(sources_str.strip("]").split(","))
8389
if any(relations):
8490
relations_to_sideload[fieldname] = set(sources_str.strip("]").split(","))
8591
except ValueError:
86-
if param in sideloadable_relations:
87-
relations_to_sideload[param] = None
92+
relations_to_sideload[param] = None
93+
8894
return relations_to_sideload
8995

96+
def check_sideload_params(self, relations_to_sideload: Dict, sideloadable_fields: Dict, prefetches: Dict):
97+
for relation, source_keys in relations_to_sideload.items():
98+
if relation not in sideloadable_fields:
99+
msg = _(f"'{relation}' is not one of the available choices.")
100+
raise ValidationError({self.sideloading_query_param_name: [msg]})
101+
if isinstance(source_keys, list):
102+
if not isinstance(prefetches.get(relation), dict):
103+
msg = _(f"'{relation}' does not have multiple sources")
104+
raise ValidationError({self.sideloading_query_param_name: [msg]})
105+
for source_key in source_keys:
106+
if source_key not in prefetches[relation].keys():
107+
msg = _(f"'{source_key}' is not one of the available source keys for relation '{relation}'")
108+
raise ValidationError({self.sideloading_query_param_name: [msg]})
109+
90110
def check_sideloading_serializer_class(self, serializer_class):
91111
if not serializer_class:
92112
raise ValueError(f"'{self.__class__.__name__}' sideloading_serializer_class not found")

0 commit comments

Comments
 (0)