1212
1313
1414class ResourceRelatedField (PrimaryKeyRelatedField ):
15+ _skip_polymorphic_optimization = True
1516 self_link_view_name = None
1617 related_link_view_name = None
1718 related_link_lookup_field = 'pk'
@@ -21,6 +22,7 @@ class ResourceRelatedField(PrimaryKeyRelatedField):
2122 'does_not_exist' : _ ('Invalid pk "{pk_value}" - object does not exist.' ),
2223 'incorrect_type' : _ ('Incorrect type. Expected resource identifier object, received {data_type}.' ),
2324 'incorrect_relation_type' : _ ('Incorrect relation type. Expected {relation_type}, received {received_type}.' ),
25+ # 'incorrect_poly_relation_type': _('Incorrect relation type. Expected one of {relation_type}, received {received_type}.'),
2426 'missing_type' : _ ('Invalid resource identifier object: missing \' type\' attribute' ),
2527 'missing_id' : _ ('Invalid resource identifier object: missing \' id\' attribute' ),
2628 'no_match' : _ ('Invalid hyperlink - No URL match.' ),
@@ -135,7 +137,8 @@ def to_internal_value(self, data):
135137 self .fail ('missing_id' )
136138
137139 if data ['type' ] != expected_relation_type :
138- self .conflict ('incorrect_relation_type' , relation_type = expected_relation_type , received_type = data ['type' ])
140+ self .conflict ('incorrect_relation_type' , relation_type = expected_relation_type ,
141+ received_type = data ['type' ])
139142
140143 return super (ResourceRelatedField , self ).to_internal_value (data ['id' ])
141144
@@ -150,7 +153,8 @@ def to_representation(self, value):
150153 resource_type = None
151154 root = getattr (self .parent , 'parent' , self .parent )
152155 field_name = self .field_name if self .field_name else self .parent .field_name
153- if getattr (root , 'included_serializers' , None ) is not None and not self .is_polymorphic :
156+ if getattr (root , 'included_serializers' , None ) is not None and \
157+ self ._skip_polymorphic_optimization :
154158 includes = get_included_serializers (root )
155159 if field_name in includes .keys ():
156160 resource_type = get_resource_type_from_serializer (includes [field_name ])
@@ -177,6 +181,42 @@ def get_choices(self, cutoff=None):
177181 ])
178182
179183
184+ class PolymorphicResourceRelatedField (ResourceRelatedField ):
185+
186+ _skip_polymorphic_optimization = False
187+ default_error_messages = dict (ResourceRelatedField .default_error_messages , ** {
188+ 'incorrect_relation_type' : _ ('Incorrect relation type. Expected one of {relation_type}, '
189+ 'received {received_type}.' ),
190+ })
191+
192+ def __init__ (self , polymorphic_serializer , * args , ** kwargs ):
193+ self .polymorphic_serializer = polymorphic_serializer
194+ super (PolymorphicResourceRelatedField , self ).__init__ (* args , ** kwargs )
195+
196+ def to_internal_value (self , data ):
197+ if isinstance (data , six .text_type ):
198+ try :
199+ data = json .loads (data )
200+ except ValueError :
201+ # show a useful error if they send a `pk` instead of resource object
202+ self .fail ('incorrect_type' , data_type = type (data ).__name__ )
203+ if not isinstance (data , dict ):
204+ self .fail ('incorrect_type' , data_type = type (data ).__name__ )
205+
206+ if 'type' not in data :
207+ self .fail ('missing_type' )
208+
209+ if 'id' not in data :
210+ self .fail ('missing_id' )
211+
212+ expected_relation_types = get_resource_type_from_serializer (self .polymorphic_serializer )
213+
214+ if data ['type' ] not in expected_relation_types :
215+ self .conflict ('incorrect_relation_type' , relation_type = ", " .join (
216+ expected_relation_types ), received_type = data ['type' ])
217+
218+ return super (ResourceRelatedField , self ).to_internal_value (data ['id' ])
219+
180220
181221class SerializerMethodResourceRelatedField (ResourceRelatedField ):
182222 """
0 commit comments