Example #1
0
    def get_request_serializer(self):
        serializer = super().get_request_serializer()

        if serializer is not None and self.method in self.implicit_body_methods:
            properties = {}
            for child_name, child in serializer.fields.items():
                if isinstance(child, (ChoiceField, WritableNestedSerializer)):
                    properties[child_name] = None
                elif isinstance(child, ManyRelatedField) and isinstance(child.child_relation, SerializedPKRelatedField):
                    properties[child_name] = None

            if properties:
                if type(serializer) not in self.writable_serializers:
                    writable_name = 'Writable' + type(serializer).__name__
                    meta_class = getattr(type(serializer), 'Meta', None)
                    if meta_class:
                        ref_name = 'Writable' + get_serializer_ref_name(serializer)
                        writable_meta = type('Meta', (meta_class,), {'ref_name': ref_name})
                        properties['Meta'] = writable_meta

                    self.writable_serializers[type(serializer)] = type(writable_name, (type(serializer),), properties)

                writable_class = self.writable_serializers[type(serializer)]
                serializer = writable_class()

        return serializer
Example #2
0
    def get_request_serializer(self):
        serializer = super().get_request_serializer()

        if serializer is not None and self.method in self.implicit_body_methods:
            properties = {}
            for child_name, child in serializer.fields.items():
                if isinstance(child, (ChoiceField, WritableNestedSerializer)):
                    properties[child_name] = None
                elif isinstance(child, ManyRelatedField) and isinstance(child.child_relation, SerializedPKRelatedField):
                    properties[child_name] = None

            if properties:
                if type(serializer) not in self.writable_serializers:
                    writable_name = 'Writable' + type(serializer).__name__
                    meta_class = getattr(type(serializer), 'Meta', None)
                    if meta_class:
                        ref_name = 'Writable' + get_serializer_ref_name(serializer)
                        writable_meta = type('Meta', (meta_class,), {'ref_name': ref_name})
                        properties['Meta'] = writable_meta

                    self.writable_serializers[type(serializer)] = type(writable_name, (type(serializer),), properties)

                writable_class = self.writable_serializers[type(serializer)]
                serializer = writable_class()

        return serializer
Example #3
0
    def get_writable_class(self, serializer):
        properties = {}
        fields = {} if hasattr(serializer, 'child') else serializer.fields
        for child_name, child in fields.items():
            if isinstance(child, (ChoiceField, WritableNestedSerializer)):
                properties[child_name] = None
            elif isinstance(child, ManyRelatedField) and isinstance(
                    child.child_relation, SerializedPKRelatedField):
                properties[child_name] = None

        if properties:
            if type(serializer) not in self.writable_serializers:
                writable_name = 'Writable' + type(serializer).__name__
                meta_class = getattr(type(serializer), 'Meta', None)
                if meta_class:
                    ref_name = 'Writable' + get_serializer_ref_name(serializer)
                    writable_meta = type('Meta', (meta_class, ),
                                         {'ref_name': ref_name})
                    properties['Meta'] = writable_meta

                self.writable_serializers[type(serializer)] = type(
                    writable_name, (type(serializer), ), properties)

            writable_class = self.writable_serializers[type(serializer)]
            return writable_class
Example #4
0
def prefetch_postprocessing_hook(result, generator, request, public):
    """ OpenAPI v3 (drf-spectacular) Some endpoints are using the PrefetchListMixin and PrefetchRetrieveMixin.
    These have nothing to do with Django prefetch_related.
    The endpoints have an @extend_schema configured with an extra parameter 'prefetch'
    This parameter contains an array of relations to prefetch. These prefetched models
    will be returned in an additional property in the response.
    The below processor ensures the result schema matches this.
    """

    serializer_classes = _get_path_to_GET_serializer_map(generator)

    paths = result.get('paths', {})
    for path in paths:
        if 'get' in paths[path]:
            for parameter in paths[path]['get']['parameters']:
                if parameter['name'] == 'prefetch':
                    prefetcher = _Prefetcher()

                    fields = _get_prefetchable_fields(
                        serializer_classes[path]())

                    field_names = [
                        name for name, field_type in fields
                        if prefetcher._find_serializer(field_type)
                    ]

                    parameter['schema']['type'] = 'array'
                    parameter['schema']['items'] = {
                        'type': "string",
                        'enum': field_names
                    }

                    field_to_serializer = dict([
                        (name, prefetcher._find_serializer(field_type))
                        for name, field_type in fields
                        if prefetcher._find_serializer(field_type)
                    ])
                    fields_to_refname = dict([
                        (name, utils.get_serializer_ref_name(serializer()))
                        for name, serializer in field_to_serializer.items()
                    ])
                    properties = dict([
                        (name,
                         dict([("type", "object"), ("readOnly", True),
                               ("additionalProperties",
                                dict([("$ref", "#/components/schemas/" +
                                       fields_to_refname[name])]))]))
                        for name in field_names
                    ])
                    ref = paths[path]['get']['responses']['200']['content'][
                        'application/json']['schema']['$ref']
                    component_name = ref.split('/')[-1]
                    result['components']['schemas'][component_name][
                        'properties']['prefetch'] = dict([("type", "object"),
                                                          ("properties",
                                                           properties)])

    return result
Example #5
0
def get_prefetch_schema(methods, serializer):
    """ Swagger / OpenAPI v2 (drf-yasg) Return a composable swagger schema that contains in the query the fields that can be prefetch from the model
        supported by the serializer and in the reponse the structure of these fields in a new top-level attribute
        named prefetch.

        Returns:
            ComposableSchema: A swagger schema
    """
    prefetcher = _Prefetcher()
    fields = _get_prefetchable_fields(serializer())

    field_to_serializer = dict([(name, prefetcher._find_serializer(field_type))
                                for name, field_type in fields
                                if prefetcher._find_serializer(field_type)])
    fields_to_refname = dict([
        (name, utils.get_serializer_ref_name(serializer()))
        for name, serializer in field_to_serializer.items()
    ])
    fields_name = [
        name for name, field_type in fields
        if prefetcher._find_serializer(field_type)
    ]

    # New openapi parameter corresponding to the prefetchable fields
    prefetch_params = [
        openapi.Parameter("prefetch",
                          in_=openapi.IN_QUERY,
                          required=False,
                          type=openapi.TYPE_ARRAY,
                          items=openapi.Items(type=openapi.TYPE_STRING,
                                              enum=fields_name))
    ]

    additional_props = dict([
        (name,
         openapi.Schema(type=openapi.TYPE_OBJECT,
                        read_only=True,
                        additional_properties=LazySchemaRef(
                            fields_to_refname[name], True)))
        for name in fields_name
    ])
    prefetch_response = {
        "200": {
            "prefetch":
            openapi.Schema(type=openapi.TYPE_OBJECT,
                           properties=additional_props)
        }
    }

    schema = extra_schema.IdentitySchema()
    for method in methods:
        schema = schema.composeWith(
            extra_schema.ExtraParameters(method, prefetch_params))
        schema = schema.composeWith(
            extra_schema.ExtraResponseField(method, prefetch_response))

    return schema
Example #6
0
    def _serializer_to_swagger_object(
        self, serializer, swagger_object_type, use_references, **kwargs
    ):
        if self.method not in ViewInspector.body_methods:
            return NotHandled

        if not any(
            isinstance(field, Base64FieldMixin) for field in serializer.fields.values()
        ):
            return NotHandled

        SwaggerType, ChildSwaggerType = self._get_partial_types(
            serializer, swagger_object_type, use_references, **kwargs
        )

        ref_name = get_serializer_ref_name(serializer)
        ref_name = f"{ref_name}Data" if ref_name else None

        def make_schema_definition():
            properties = OrderedDict()
            required = []
            for property_name, child in serializer.fields.items():
                prop_kwargs = {"read_only": bool(child.read_only) or None}
                prop_kwargs = filter_none(prop_kwargs)

                child_schema = self.probe_field_inspectors(
                    child, ChildSwaggerType, use_references, **prop_kwargs
                )
                properties[property_name] = child_schema

                if child.required and not getattr(child_schema, "read_only", False):
                    required.append(property_name)

            result = SwaggerType(
                type=openapi.TYPE_OBJECT,
                properties=properties,
                required=required or None,
            )
            if not ref_name and "title" in result:
                # on an inline model, the title is derived from the field name
                # but is visually displayed like the model name, which is confusing
                # it is better to just remove title from inline models
                del result.title

            # Provide an option to add manual paremeters to a schema
            # for example, to add examples
            # self.add_manual_fields(serializer, result)
            return self.process_result(result, None, None)

        if not ref_name or not use_references:
            return make_schema_definition()

        definitions = self.components.with_scope(openapi.SCHEMA_DEFINITIONS)
        definitions.setdefault(ref_name, make_schema_definition)
        return openapi.SchemaRef(definitions, ref_name)