44from typing import Dict , Optional , Union , Tuple , Set
55
66from django .db .models import Prefetch
7+ from django .utils .translation import gettext_lazy as _
78from rest_framework .exceptions import ValidationError
89from rest_framework .generics import get_object_or_404
9- from rest_framework .renderers import BrowsableAPIRenderer
1010from rest_framework .response import Response
1111from 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