Skip to content

Commit

Permalink
Merge pull request #1009 from tfranzel/pydantic_blockers
Browse files Browse the repository at this point in the history
Example Pydantic support
  • Loading branch information
tfranzel authored Jun 19, 2023
2 parents c74f607 + 9bae4f0 commit ae52f23
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 4 deletions.
11 changes: 10 additions & 1 deletion docs/blueprints.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,13 @@ django-parler-rest
`django-parler-rest <https://github.com/django-parler/django-parler-rest>`_ integration for
translation package `django-parler <https://github.com/django-parler/django-parler>`_.

.. literalinclude:: blueprints/django_parler_rest.py
.. literalinclude:: blueprints/django_parler_rest.py


Pydantic
--------

Preliminary support for `Pydantic <https://github.com/pydantic/pydantic>`_ models. This may or may not
end up in the main package. Catches decorated Pydantic classes and integrates their schema.

.. literalinclude:: blueprints/pydantic.py
36 changes: 36 additions & 0 deletions docs/blueprints/pydantic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from drf_spectacular.extensions import OpenApiSerializerExtension
from drf_spectacular.plumbing import ResolvedComponent


class PydanticExtension(OpenApiSerializerExtension):
target_class = 'pydantic.BaseModel'
match_subclasses = True

def get_name(self, auto_schema, direction):
return self.target.__name__

def map_serializer(self, auto_schema, direction):
def translate_refs(obj, key=None):
if isinstance(obj, dict):
return {k: translate_refs(v, k) for k, v in obj.items()}
elif isinstance(obj, list):
return [translate_refs(i) for i in obj]
elif key == '$ref':
return obj.replace('#/definitions/', '#/components/schemas/')
else:
return obj

# let pydantic generate a JSON schema
schema = self.target.schema()

# pull out potential sub-schemas and put them into component section
for sub_name, sub_schema in schema.pop('definitions', {}).items():
component = ResolvedComponent(
name=sub_name,
type=ResolvedComponent.SCHEMA,
object=sub_name,
schema=translate_refs(sub_schema),
)
auto_schema.registry.register_on_missing(component)

return translate_refs(schema)
10 changes: 7 additions & 3 deletions drf_spectacular/plumbing.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ def is_basic_type(obj, allow_none=True):
def is_patched_serializer(serializer, direction):
return bool(
spectacular_settings.COMPONENT_SPLIT_PATCH
and serializer.partial
and not serializer.read_only
and getattr(serializer, 'partial', None)
and not getattr(serializer, 'read_only', None)
and not (spectacular_settings.COMPONENT_SPLIT_REQUEST and direction == 'response')
)

Expand Down Expand Up @@ -764,7 +764,11 @@ def _matches(cls, target) -> bool:
if cls.target_class is None:
return False # app not installed
elif cls.match_subclasses:
return issubclass(get_class(target), cls.target_class) # type: ignore
# Targets may trigger customized check through __subclasscheck__. Attempt to be more robust
try:
return issubclass(get_class(target), cls.target_class) # type: ignore
except TypeError:
return False
else:
return get_class(target) == cls.target_class

Expand Down

0 comments on commit ae52f23

Please sign in to comment.