def __init__(self, *args, **kwargs): super(SparseFieldsetsMixin, self).__init__(*args, **kwargs) context = kwargs.get('context') request = context.get('request') if context else None if request: sparse_fieldset_query_param = 'fields[{}]'.format( get_resource_type_from_serializer(self) ) try: param_name = next( key for key in request.query_params if sparse_fieldset_query_param in key ) except StopIteration: pass else: fieldset = request.query_params.get(param_name).split(',') # iterate over a *copy* of self.fields' underlying OrderedDict, because we may # modify the original during the iteration. # self.fields is a `rest_framework.utils.serializer_helpers.BindingDict` for field_name, field in self.fields.fields.copy().items(): if field_name == api_settings.URL_FIELD_NAME: # leave self link there continue if field_name not in fieldset: self.fields.pop(field_name)
def __new__(cls, name, bases, attrs): new_class = super(PolymorphicSerializerMetaclass, cls).__new__(cls, name, bases, attrs) # Ensure initialization is only performed for subclasses of PolymorphicModelSerializer # (excluding PolymorphicModelSerializer class itself). parents = [b for b in bases if isinstance(b, PolymorphicSerializerMetaclass)] if not parents: return new_class polymorphic_serializers = getattr(new_class, 'polymorphic_serializers', None) if not polymorphic_serializers: raise NotImplementedError( "A PolymorphicModelSerializer must define a `polymorphic_serializers` attribute.") serializer_to_model = { serializer: serializer.Meta.model for serializer in polymorphic_serializers} model_to_serializer = { serializer.Meta.model: serializer for serializer in polymorphic_serializers} type_to_serializer = { get_resource_type_from_serializer(serializer): serializer for serializer in polymorphic_serializers} new_class._poly_serializer_model_map = serializer_to_model new_class._poly_model_serializer_map = model_to_serializer new_class._poly_type_serializer_map = type_to_serializer new_class._poly_force_type_resolution = True # Flag each linked polymorphic serializer to force type resolution based on instance for serializer in polymorphic_serializers: serializer._poly_force_type_resolution = True return new_class
def _get_field_representation(self, field, instance): request = self.context.get('request') is_included = field.source in get_included_resources(request) if not is_included and \ isinstance(field, ModelSerializer) and \ hasattr(instance, field.source + '_id'): attribute = getattr(instance, field.source + '_id') if attribute is None: return None resource_type = get_resource_type_from_serializer(field) if resource_type: return OrderedDict([('type', resource_type), ('id', attribute)]) attribute = field.get_attribute(instance) # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: return None else: return field.to_representation(attribute)
def to_representation(self, value): if getattr(self, 'pk_field', None) is not None: pk = self.pk_field.to_representation(value.pk) else: pk = value.pk # check to see if this resource has a different resource_name when # included and use that name resource_type = None root = getattr(self.parent, 'parent', self.parent) field_name = self.field_name if self.field_name else self.parent.field_name if getattr(root, 'included_serializers', None) is not None: includes = get_included_serializers(root) if field_name in includes.keys(): resource_type = get_resource_type_from_serializer(includes[field_name]) resource_type = resource_type if resource_type else get_resource_type_from_instance(value) return OrderedDict([('type', resource_type), ('id', str(pk))])
def __init__(self, *args, **kwargs): super(SparseFieldsetsMixin, self).__init__(*args, **kwargs) context = kwargs.get('context') request = context.get('request') if context else None if request: sparse_fieldset_query_param = 'fields[{}]'.format( get_resource_type_from_serializer(self)) try: param_name = next(key for key in request.query_params if sparse_fieldset_query_param in key) except StopIteration: pass else: fieldset = request.query_params.get(param_name).split(',') # iterate over a *copy* of self.fields' underlying OrderedDict, because we may # modify the original during the iteration. # self.fields is a `rest_framework.utils.serializer_helpers.BindingDict` for field_name, field in self.fields.fields.copy().items(): if field_name == api_settings.URL_FIELD_NAME: # leave self link there continue if field_name not in fieldset: self.fields.pop(field_name)
def __new__(cls, name, bases, attrs): new_class = super(PolymorphicSerializerMetaclass, cls).__new__( cls, name, bases, attrs ) # Ensure initialization is only performed for subclasses of PolymorphicModelSerializer # (excluding PolymorphicModelSerializer class itself). parents = [b for b in bases if isinstance(b, PolymorphicSerializerMetaclass)] if not parents: return new_class polymorphic_serializers = getattr(new_class, "polymorphic_serializers", None) if not polymorphic_serializers: raise NotImplementedError( "A PolymorphicModelSerializer must define a `polymorphic_serializers` attribute." ) serializer_to_model = { serializer: serializer.Meta.model for serializer in polymorphic_serializers } model_to_serializer = { serializer.Meta.model: serializer for serializer in polymorphic_serializers } type_to_serializer = { get_resource_type_from_serializer(serializer): serializer for serializer in polymorphic_serializers } new_class._poly_serializer_model_map = serializer_to_model new_class._poly_model_serializer_map = model_to_serializer new_class._poly_type_serializer_map = type_to_serializer new_class._poly_force_type_resolution = True # Flag each linked polymorphic serializer to force type resolution based on instance for serializer in polymorphic_serializers: serializer._poly_force_type_resolution = True return new_class
def get_resource_name_from_related_id_field(self, field_name, id_field): # Unpack ManyRelatedField from many wrapper id_field = getattr(id_field, 'child_relation', None) or id_field # Not very frequent case but different from others if isinstance(id_field, serializers.Serializer): return json_api_utils.get_resource_type_from_serializer(id_field) # Most cases else: # Try to get from included serializers parent_serializer = get_parent_serializer(id_field) if isinstance(id_field, dja_serializers.ResourceRelatedField): resource_name = id_field.get_resource_type_from_included_serializer( ) if resource_name: return resource_name related_model = get_field_related_model(id_field) if related_model: return get_resource_type_from_model(related_model) raise ValueError( f"Unable to extract resource name for {parent_serializer}.{field_name} serializer field" )
def field_to_swagger_object(self, field, swagger_object_type, use_references, included=False, is_request=None, **kwargs): if not self.is_json_api_root_serializer(field, is_request): return super().field_to_swagger_object(field, swagger_object_type, use_references, **kwargs) if is_request is None and included: is_request = False if included: resource_name = get_resource_type_from_serializer(field) else: resource_name = get_resource_name(context={'view': self.view}) SwaggerType, ChildSwaggerType = self._get_partial_types( field, swagger_object_type, use_references, **kwargs) return self.build_serializer_schema(field, resource_name, SwaggerType, ChildSwaggerType, use_references, is_request)
def get_resource_type_from_included_serializer(self, value): """ Check to see it this resource has a different resource_name when included and return that name, or None """ field_name = self.field_name or self.parent.field_name parent = self.get_parent_serializer() if parent is not None: # accept both singular and plural versions of field_name field_names = [ inflection.singularize(field_name), inflection.pluralize(field_name) ] includes = get_generic_included_serializers(parent) for field in field_names: if field in includes.keys(): for serializer in includes[field]: if isinstance(value, dict) and value.get('type', None) == serializer.Meta.resource_name: return serializer.Meta.resource_name elif isinstance(value, (serializer.Meta.model,)): return get_resource_type_from_serializer(serializer) return None
def extract_included(cls, fields, resource, resource_instance, included_resources, included_cache): """ Adds related data to the top level included key when the request includes ?include=example,example_field2 """ # this function may be called with an empty record (example: Browsable Interface) if not resource_instance: return current_serializer = fields.serializer context = current_serializer.context included_serializers = getattr(current_serializer, "included_serializers", dict()) included_resources = copy.copy(included_resources) included_resources = [ inflection.underscore(value) for value in included_resources ] for field_name, field in iter(fields.items()): # Skip URL field if field_name == api_settings.URL_FIELD_NAME: continue # Skip fields without relations if not utils.is_relationship_field(field): continue try: included_resources.remove(field_name) except ValueError: # Skip fields not in requested included resources # If no child field, directly continue with the next field if field_name not in [ node.split(".")[0] for node in included_resources ]: continue relation_instance = cls.extract_relation_instance( field, resource_instance) if isinstance(relation_instance, Manager): relation_instance = relation_instance.all() serializer_data = resource.get(field_name) if isinstance(field, relations.ManyRelatedField): serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=True, context=context) serializer_data = field.data if isinstance(field, relations.RelatedField): if relation_instance is None or not serializer_data: continue many = field._kwargs.get("child_relation", None) is not None if isinstance(field, ResourceRelatedField) and not many: already_included = ( serializer_data["type"] in included_cache and serializer_data["id"] in included_cache[serializer_data["type"]]) if already_included: continue serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=many, context=context) serializer_data = field.data new_included_resources = [ key.replace("%s." % field_name, "", 1) for key in included_resources if field_name == key.split(".")[0] ] if isinstance(field, ListSerializer): serializer = field.child relation_type = utils.get_resource_type_from_serializer( serializer) relation_queryset = list(relation_instance) if serializer_data: for position in range(len(serializer_data)): serializer_resource = serializer_data[position] nested_resource_instance = relation_queryset[position] resource_type = (relation_type or utils.get_resource_type_from_instance( nested_resource_instance)) serializer_fields = utils.get_serializer_fields( serializer.__class__(nested_resource_instance, context=serializer.context)) new_item = cls.build_json_resource_obj( serializer_fields, serializer_resource, nested_resource_instance, resource_type, serializer, getattr(serializer, "_poly_force_type_resolution", False), ) included_cache[new_item["type"]][ new_item["id"]] = new_item cls.extract_included( serializer_fields, serializer_resource, nested_resource_instance, new_included_resources, included_cache, ) if isinstance(field, Serializer): relation_type = utils.get_resource_type_from_serializer(field) # Get the serializer fields serializer_fields = utils.get_serializer_fields(field) if serializer_data: new_item = cls.build_json_resource_obj( serializer_fields, serializer_data, relation_instance, relation_type, field, getattr(field, "_poly_force_type_resolution", False), ) included_cache[new_item["type"]][new_item["id"]] = new_item cls.extract_included( serializer_fields, serializer_data, relation_instance, new_included_resources, included_cache, )
def extract_included(cls, fields, resource, resource_instance, included_resources, included_cache): """ Adds related data to the top level included key when the request includes ?include=example,example_field2 """ # this function may be called with an empty record (example: Browsable Interface) if not resource_instance: return current_serializer = fields.serializer context = current_serializer.context included_serializers = utils.get_included_serializers(current_serializer) included_resources = copy.copy(included_resources) included_resources = [inflection.underscore(value) for value in included_resources] for field_name, field in six.iteritems(fields): # Skip URL field if field_name == api_settings.URL_FIELD_NAME: continue # Skip fields without relations or serialized data if not isinstance( field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer) ): continue try: included_resources.remove(field_name) except ValueError: # Skip fields not in requested included resources # If no child field, directly continue with the next field if field_name not in [node.split('.')[0] for node in included_resources]: continue relation_instance = cls.extract_relation_instance( field, resource_instance ) if isinstance(relation_instance, Manager): relation_instance = relation_instance.all() serializer_data = resource.get(field_name) if isinstance(field, relations.ManyRelatedField): serializer_class = included_serializers[field_name] context['parent'] = resource_instance field = serializer_class(relation_instance, many=True, context=context) serializer_data = field.data if isinstance(field, relations.RelatedField): if relation_instance is None or not serializer_data: continue many = field._kwargs.get('child_relation', None) is not None if isinstance(field, ResourceRelatedField) and not many: already_included = serializer_data['type'] in included_cache and \ serializer_data['id'] in included_cache[serializer_data['type']] if already_included: continue serializer_class = included_serializers[field_name] context['parent'] = resource_instance field = serializer_class(relation_instance, many=many, context=context) serializer_data = field.data new_included_resources = [key.replace('%s.' % field_name, '', 1) for key in included_resources if field_name == key.split('.')[0]] if isinstance(field, ListSerializer): serializer = field.child relation_type = utils.get_resource_type_from_serializer(serializer) relation_queryset = list(relation_instance) if serializer_data: for position in range(len(serializer_data)): serializer_resource = serializer_data[position] nested_resource_instance = relation_queryset[position] resource_type = ( relation_type or utils.get_resource_type_from_instance(nested_resource_instance) ) serializer_fields = utils.get_serializer_fields( serializer.__class__( nested_resource_instance, context=serializer.context ) ) new_item = cls.build_json_resource_obj( serializer_fields, serializer_resource, nested_resource_instance, resource_type, getattr(serializer, '_poly_force_type_resolution', False) ) # NEW: Add meta to included resource meta = cls.extract_meta(serializer.__class__, serializer_resource) if meta: new_item.update({'meta': utils._format_object(meta)}) included_cache[new_item['type']][new_item['id']] = \ utils._format_object(new_item) cls.extract_included( serializer_fields, serializer_resource, nested_resource_instance, new_included_resources, included_cache, ) if isinstance(field, Serializer): relation_type = utils.get_resource_type_from_serializer(field) # Get the serializer fields serializer_fields = utils.get_serializer_fields(field) new_item = cls.build_json_resource_obj( serializer_fields, serializer_data, relation_instance, relation_type, getattr(field, '_poly_force_type_resolution', False) ) # NEW: Add meta to included resource meta = cls.extract_meta(serializer_class, serializer_data) if meta: new_item.update({'meta': utils._format_object(meta)}) included_cache[new_item['type']][new_item['id']] = utils._format_object( new_item ) cls.extract_included( serializer_fields, serializer_data, relation_instance, new_included_resources, included_cache, )
def extract_included(cls, fields, resource, resource_instance, included_resources): # this function may be called with an empty record (example: Browsable Interface) if not resource_instance: return included_data = list() current_serializer = fields.serializer context = current_serializer.context included_serializers = utils.get_included_serializers( current_serializer) included_resources = copy.copy(included_resources) included_resources = [ inflection.underscore(value) for value in included_resources ] for field_name, field in six.iteritems(fields): # Skip URL field if field_name == api_settings.URL_FIELD_NAME: continue # Skip fields without relations or serialized data if not isinstance(field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer)): continue try: included_resources.remove(field_name) except ValueError: # Skip fields not in requested included resources # If no child field, directly continue with the next field if field_name not in [ node.split('.')[0] for node in included_resources ]: continue try: relation_instance = getattr(resource_instance, field_name) except AttributeError: try: # For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source` relation_instance = getattr(resource_instance, field.child_relation.source) except AttributeError: if not hasattr(current_serializer, field.source): continue serializer_method = getattr(current_serializer, field.source) relation_instance = serializer_method(resource_instance) if isinstance(relation_instance, Manager): relation_instance = relation_instance.all() new_included_resources = [ key.replace('%s.' % field_name, '', 1) for key in included_resources if field_name == key.split('.')[0] ] serializer_data = resource.get(field_name) if isinstance(field, relations.ManyRelatedField): serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=True, context=context) serializer_data = field.data if isinstance(field, relations.RelatedField): if relation_instance is None: continue many = field._kwargs.get('child_relation', None) is not None serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=many, context=context) serializer_data = field.data if isinstance(field, ListSerializer): serializer = field.child relation_type = utils.get_resource_type_from_serializer( serializer) relation_queryset = list(relation_instance) # Get the serializer fields serializer_fields = utils.get_serializer_fields(serializer) if serializer_data: for position in range(len(serializer_data)): serializer_resource = serializer_data[position] nested_resource_instance = relation_queryset[position] resource_type = (relation_type or utils.get_resource_type_from_instance( nested_resource_instance)) included_data.append( cls.build_json_resource_obj( serializer_fields, serializer_resource, nested_resource_instance, resource_type)) included_data.extend( cls.extract_included(serializer_fields, serializer_resource, nested_resource_instance, new_included_resources)) if isinstance(field, Serializer): relation_type = utils.get_resource_type_from_serializer(field) # Get the serializer fields serializer_fields = utils.get_serializer_fields(field) if serializer_data: included_data.append( cls.build_json_resource_obj(serializer_fields, serializer_data, relation_instance, relation_type)) included_data.extend( cls.extract_included(serializer_fields, serializer_data, relation_instance, new_included_resources)) return api_utils.format_keys(included_data)
def extract_included(cls, request, fields, resource, resource_instance, included_resources): # this function may be called with an empty record (example: Browsable Interface) if not resource_instance: return included_data = list() current_serializer = fields.serializer context = current_serializer.context included_serializers = utils.get_included_serializers( current_serializer) included_resources = copy.copy(included_resources) included_resources = [ inflection.underscore(value) for value in included_resources ] for field_name, field in six.iteritems(fields): # Skip URL field if field_name == api_settings.URL_FIELD_NAME: continue # Skip fields without relations or serialized data if not isinstance(field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer)): continue try: included_resources.remove(field_name) except ValueError: # Skip fields not in requested included resources # If no child field, directly continue with the next field if field_name not in [ node.split('.')[0] for node in included_resources ]: continue try: relation_instance = getattr(resource_instance, field_name) except AttributeError: try: # For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source` relation_instance = getattr(resource_instance, field.child_relation.source) except AttributeError: if not hasattr(current_serializer, field.source): continue serializer_method = getattr(current_serializer, field.source) relation_instance = serializer_method(resource_instance) if isinstance(relation_instance, Manager): relation_instance = relation_instance.all() new_included_resources = [ key.replace('%s.' % field_name, '', 1) for key in included_resources if field_name == key.split('.')[0] ] serializer_data = resource.get(field_name) if isinstance(field, RemoteResourceField): user_id = getattr(request.user, 'id', None) roles = request.user.roles pk = serializer_data.get('id') include = ",".join(new_included_resources) try: remote_resource = event_client.get_remote_resource_data( field_name, pk=pk, user_id=user_id, include=include, page_size=1000, roles=roles) body = ujson.loads(remote_resource['body']) if 400 <= remote_resource['status'] < 600: raise RemoteResourceIncludeError( field_name, body["errors"][0]) except RequestTimeout: raise RemoteResourceIncludeTimeoutError(field_name) included_data.append(body['data']) if body.get('included'): included_data.extend(body['included']) # We continue here since RemoteResourceField inherits # form ResourceRelatedField which is a RelatedField continue if isinstance(field, relations.ManyRelatedField): serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=True, context=context) serializer_data = field.data if isinstance(field, relations.RelatedField): if relation_instance is None: continue many = field._kwargs.get('child_relation', None) is not None serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=many, context=context) serializer_data = field.data if isinstance(field, ListSerializer): serializer = field.child relation_type = utils.get_resource_type_from_serializer( serializer) relation_queryset = list(relation_instance) # Get the serializer fields serializer_fields = utils.get_serializer_fields(serializer) if serializer_data: for position in range(len(serializer_data)): serializer_resource = serializer_data[position] nested_resource_instance = relation_queryset[position] resource_type = (relation_type or utils.get_resource_type_from_instance( nested_resource_instance)) included_data.append( cls.build_json_resource_obj( serializer_fields, serializer_resource, nested_resource_instance, resource_type)) included_data.extend( cls.extract_included(request, serializer_fields, serializer_resource, nested_resource_instance, new_included_resources)) if isinstance(field, Serializer): relation_type = utils.get_resource_type_from_serializer(field) # Get the serializer fields serializer_fields = utils.get_serializer_fields(field) if serializer_data: included_data.append( cls.build_json_resource_obj(serializer_fields, serializer_data, relation_instance, relation_type)) included_data.extend( cls.extract_included(request, serializer_fields, serializer_data, relation_instance, new_included_resources)) return key_formatter()(included_data)
def _create_resource(cls, serializer_class, *args, **kwargs): """Make the resource object for related fields.""" serializer = cls._create(serializer_class, *args, **kwargs) resource_type = get_resource_type_from_serializer(serializer_class) return {"type": resource_type, "id": str(serializer.instance.pk)}
def extract_included(cls, fields, resource, resource_instance, included_resources, included_cache): """ Adds related data to the top level included key when the request includes ?include=example,example_field2 """ # this function may be called with an empty record (example: Browsable Interface) if not resource_instance: return current_serializer = fields.serializer context = current_serializer.context included_serializers = utils.get_included_serializers(current_serializer) included_resources = copy.copy(included_resources) included_resources = [inflection.underscore(value) for value in included_resources] for field_name, field in six.iteritems(fields): # Skip URL field if field_name == api_settings.URL_FIELD_NAME: continue # Skip fields without relations or serialized data if not isinstance( field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer) ): continue try: included_resources.remove(field_name) except ValueError: # Skip fields not in requested included resources # If no child field, directly continue with the next field if field_name not in [node.split('.')[0] for node in included_resources]: continue relation_instance = cls.extract_relation_instance( field_name, field, resource_instance, current_serializer ) if isinstance(relation_instance, Manager): relation_instance = relation_instance.all() serializer_data = resource.get(field_name) if isinstance(field, relations.ManyRelatedField): serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=True, context=context) serializer_data = field.data if isinstance(field, relations.RelatedField): if relation_instance is None or not serializer_data: continue many = field._kwargs.get('child_relation', None) is not None if isinstance(field, ResourceRelatedField) and not many: already_included = serializer_data['type'] in included_cache and \ serializer_data['id'] in included_cache[serializer_data['type']] if already_included: continue serializer_class = included_serializers[field_name] field = serializer_class(relation_instance, many=many, context=context) serializer_data = field.data new_included_resources = [key.replace('%s.' % field_name, '', 1) for key in included_resources if field_name == key.split('.')[0]] if isinstance(field, ListSerializer): serializer = field.child relation_type = utils.get_resource_type_from_serializer(serializer) relation_queryset = list(relation_instance) if serializer_data: for position in range(len(serializer_data)): serializer_resource = serializer_data[position] nested_resource_instance = relation_queryset[position] resource_type = ( relation_type or utils.get_resource_type_from_instance(nested_resource_instance) ) serializer_fields = utils.get_serializer_fields( serializer.__class__( nested_resource_instance, context=serializer.context ) ) new_item = cls.build_json_resource_obj( serializer_fields, serializer_resource, nested_resource_instance, resource_type, getattr(serializer, '_poly_force_type_resolution', False) ) included_cache[new_item['type']][new_item['id']] = \ utils._format_object(new_item) cls.extract_included( serializer_fields, serializer_resource, nested_resource_instance, new_included_resources, included_cache, ) if isinstance(field, Serializer): relation_type = utils.get_resource_type_from_serializer(field) # Get the serializer fields serializer_fields = utils.get_serializer_fields(field) if serializer_data: new_item = cls.build_json_resource_obj( serializer_fields, serializer_data, relation_instance, relation_type, getattr(field, '_poly_force_type_resolution', False) ) included_cache[new_item['type']][new_item['id']] = utils._format_object( new_item ) cls.extract_included( serializer_fields, serializer_data, relation_instance, new_included_resources, included_cache, )