diff --git a/docs/changelog.rst b/docs/changelog.rst index e9d1968..a7895db 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,18 @@ Changelog ######### +********* +**1.8.0** +********* + +*Release date: Jun 01, 2018* + +- **ADDED:** added a :ref:`swagger_schema_fields ` field on serializer ``Meta`` classes for + customizing schema generation +- **FIXED:** error responses from schema views are now rendered with ``JSONRenderer`` instead of throwing + confusing errors (:pr:`130`, :issue:`58`) +- **FIXED:** ``readOnly`` schema fields will now no longer be marked as ``required`` (:pr:`133`) + ********* **1.7.4** ********* diff --git a/docs/custom_spec.rst b/docs/custom_spec.rst index 0c08ca3..cc04f47 100644 --- a/docs/custom_spec.rst +++ b/docs/custom_spec.rst @@ -169,13 +169,19 @@ You can define some per-serializer options by adding a ``Meta`` class to your se class Meta: ... options here ... -Currently, the only option you can add here is +.. _swagger_schema_fields: + +The available options are: * ``ref_name`` - a string which will be used as the model definition name for this serializer class; setting it to ``None`` will force the serializer to be generated as an inline model everywhere it is used. If two serializers have the same ``ref_name``, both their usages will be replaced with a reference to the same definition. If this option is not specified, all serializers have an implicit name derived from their class name, minus any ``Serializer`` suffix (e.g. ``UserSerializer`` -> ``User``, ``SerializerWithSuffix`` -> ``SerializerWithSuffix``) + * ``swagger_schema_fields`` - a dictionary mapping :class:`.Schema` field names to values. These attributes + will be set on the :class:`.Schema` object generated from the ``Serializer``. Field names must be python values, + which are converted to Swagger ``Schema`` attribute names according to :func:`.make_swagger_name`. + Attribute names and values must conform to the `OpenAPI 2.0 specification `_. ************************* diff --git a/src/drf_yasg/inspectors/field.py b/src/drf_yasg/inspectors/field.py index 1ee2178..48ac582 100644 --- a/src/drf_yasg/inspectors/field.py +++ b/src/drf_yasg/inspectors/field.py @@ -22,12 +22,39 @@ class InlineSerializerInspector(SerializerInspector): #: whether to output :class:`.Schema` definitions inline or into the ``definitions`` section use_definitions = False + def add_manual_fields(self, serializer, schema): + """Set fields from the ``swagger_schem_fields`` attribute on the serializer's Meta class. This method is called + only for serializers that are converted into ``openapi.Schema`` objects. + + :param serializer: serializer instance + :param openapi.Schema schema: the schema object to be modified in-place + """ + serializer_meta = getattr(serializer, 'Meta', None) + swagger_schema_fields = getattr(serializer_meta, 'swagger_schema_fields', {}) + if swagger_schema_fields: + for attr, val in swagger_schema_fields.items(): + setattr(schema, attr, val) + def get_schema(self, serializer): - return self.probe_field_inspectors(serializer, openapi.Schema, self.use_definitions) + result = self.probe_field_inspectors(serializer, openapi.Schema, self.use_definitions) + schema = openapi.resolve_ref(result, self.components) + self.add_manual_fields(serializer, schema) + return result + + def add_manual_parameters(self, serializer, parameters): + """Add/replace parameters from the given list of automatically generated request parameters. This method + is called only when the serializer is converted into a list of parameters for use in a form data request. + + :param serializer: serializer instance + :param list[openapi.Parameter] parameters: genereated parameters + :return: modified parameters + :rtype: list[openapi.Parameter] + """ + return parameters def get_request_parameters(self, serializer, in_): fields = getattr(serializer, 'fields', {}) - return [ + parameters = [ self.probe_field_inspectors( value, openapi.Parameter, self.use_definitions, name=self.get_parameter_name(key), in_=in_ @@ -36,12 +63,17 @@ class InlineSerializerInspector(SerializerInspector): in fields.items() ] + return self.add_manual_parameters(serializer, parameters) + def get_property_name(self, field_name): return field_name def get_parameter_name(self, field_name): return field_name + def get_serializer_ref_name(self, serializer): + return get_serializer_ref_name(serializer) + def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) @@ -55,7 +87,7 @@ class InlineSerializerInspector(SerializerInspector): if swagger_object_type != openapi.Schema: raise SwaggerGenerationError("cannot instantiate nested serializer as " + swagger_object_type.__name__) - ref_name = get_serializer_ref_name(field) + ref_name = self.get_serializer_ref_name(field) def make_schema_definition(): properties = OrderedDict() diff --git a/testproj/todo/serializer.py b/testproj/todo/serializer.py index ede243c..c831395 100644 --- a/testproj/todo/serializer.py +++ b/testproj/todo/serializer.py @@ -1,3 +1,5 @@ +from collections import OrderedDict + from django.utils import timezone from rest_framework import serializers from rest_framework_recursive.fields import RecursiveField @@ -26,6 +28,15 @@ class TodoYetAnotherSerializer(serializers.ModelSerializer): model = TodoYetAnother fields = ('title', 'todo') depth = 2 + swagger_schema_fields = { + 'example': OrderedDict([ + ('title', 'parent'), + ('todo', OrderedDict([ + ('title', 'child'), + ('todo', None), + ])), + ]) + } class TodoTreeSerializer(serializers.ModelSerializer): diff --git a/tests/reference.yaml b/tests/reference.yaml index 66a9d6a..6b41d07 100644 --- a/tests/reference.yaml +++ b/tests/reference.yaml @@ -1578,6 +1578,11 @@ definitions: minLength: 1 readOnly: true readOnly: true + example: + title: parent + todo: + title: child + todo: null UserSerializerrr: required: - username