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
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
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
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
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
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)