Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional types from the specification. #526

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions docs/drf_yasg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ provides the :py:class:`~drf_spectacular.types.OpenApiTypes` enum:
- :py:data:`~drf_yasg.openapi.TYPE_STRING` with :py:data:`~drf_yasg.openapi.FORMAT_EMAIL` is called
:py:attr:`~drf_spectacular.types.OpenApiTypes.EMAIL`
- :py:data:`~drf_yasg.openapi.TYPE_STRING` with :py:data:`~drf_yasg.openapi.FORMAT_IPV4` is called
:py:attr:`~drf_spectacular.types.OpenApiTypes.IP4`
:py:attr:`~drf_spectacular.types.OpenApiTypes.IP4`, but you can use :py:class:`ipaddress.IPv4Address`.
- :py:data:`~drf_yasg.openapi.TYPE_STRING` with :py:data:`~drf_yasg.openapi.FORMAT_IPV6` is called
:py:attr:`~drf_spectacular.types.OpenApiTypes.IP6`
:py:attr:`~drf_spectacular.types.OpenApiTypes.IP6`, but you can use :py:class:`ipaddress.IPv6Address`.
- :py:data:`~drf_yasg.openapi.TYPE_STRING` with :py:data:`~drf_yasg.openapi.FORMAT_PASSWORD` is called
:py:attr:`~drf_spectacular.types.OpenApiTypes.PASSWORD`
- :py:data:`~drf_yasg.openapi.TYPE_STRING` with :py:data:`~drf_yasg.openapi.FORMAT_URI` is called
Expand All @@ -120,10 +120,19 @@ provides the :py:class:`~drf_spectacular.types.OpenApiTypes` enum:
- The following additional types are also available:

- :py:attr:`~drf_spectacular.types.OpenApiTypes.ANY` for which you can use :py:class:`typing.Any`.
- :py:attr:`~drf_spectacular.types.OpenApiTypes.DURATION`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.DURATION` for which you can use :py:class:`datetime.timedelta`.
- :py:attr:`~drf_spectacular.types.OpenApiTypes.HOSTNAME`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.IDN_EMAIL`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.IDN_HOSTNAME`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.IRI_REF`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.IRI`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.JSON_PTR_REL`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.JSON_PTR`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.NONE` for which you can use :py:data:`None`.
- :py:attr:`~drf_spectacular.types.OpenApiTypes.TIME`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.REGEX` for which you can use :py:class:`typing.Pattern`.
- :py:attr:`~drf_spectacular.types.OpenApiTypes.TIME` for which you can use :py:class:`datetime.time`.
- :py:attr:`~drf_spectacular.types.OpenApiTypes.URI_REF`
- :py:attr:`~drf_spectacular.types.OpenApiTypes.URI_TPL`

Parameter Location
------------------
Expand Down
50 changes: 43 additions & 7 deletions drf_spectacular/types.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import enum
import typing
from datetime import date, datetime
from datetime import date, datetime, time, timedelta
from decimal import Decimal
from ipaddress import IPv4Address, IPv6Address
from typing import Any, Pattern, Type, Union
from uuid import UUID

_KnownPythonTypes = typing.Type[
typing.Union[str, float, bool, bytes, int, UUID, Decimal, datetime, date, dict],
]
_KnownPythonTypes = Type[Union[
str, float, bool, bytes, int, UUID, Decimal, datetime, date, time, timedelta, IPv4Address, IPv6Address, dict, Any,
]]


class OpenApiTypes(enum.Enum):
Expand Down Expand Up @@ -51,12 +52,24 @@ class OpenApiTypes(enum.Enum):
UUID = enum.auto()
#: Converted to ``{"type": "string", "format": "uri"}``.
URI = enum.auto()
#: Converted to ``{"type": "string", "format": "uri-reference"}``.
URI_REF = enum.auto()
#: Converted to ``{"type": "string", "format": "uri-template"}``.
URI_TPL = enum.auto()
#: Converted to ``{"type": "string", "format": "iri"}``.
IRI = enum.auto()
#: Converted to ``{"type": "string", "format": "iri-reference"}``.
IRI_REF = enum.auto()
#: Converted to ``{"type": "string", "format": "ipv4"}``.
#: Equivalent to :py:class:`~ipaddress.IPv4Address`.
IP4 = enum.auto()
#: Converted to ``{"type": "string", "format": "ipv6"}``.
#: Equivalent to :py:class:`~ipaddress.IPv6Address`.
IP6 = enum.auto()
#: Converted to ``{"type": "string", "format": "hostname"}``.
HOSTNAME = enum.auto()
#: Converted to ``{"type": "string", "format": "idn-hostname"}``.
IDN_HOSTNAME = enum.auto()
#: Converted to ``{"type": "number", "format": "double"}``.
#: The same as :py:attr:`~drf_spectacular.types.OpenApiTypes.DOUBLE`.
#: Equivalent to :py:class:`~decimal.Decimal`.
Expand All @@ -76,6 +89,15 @@ class OpenApiTypes(enum.Enum):
DURATION = enum.auto()
#: Converted to ``{"type": "string", "format": "email"}``.
EMAIL = enum.auto()
#: Converted to ``{"type": "string", "format": "idn-email"}``.
IDN_EMAIL = enum.auto()
#: Converted to ``{"type": "string", "format": "json-pointer"}``.
JSON_PTR = enum.auto()
#: Converted to ``{"type": "string", "format": "relative-json-pointer"}``.
JSON_PTR_REL = enum.auto()
#: Converted to ``{"type": "string", "format": "regex"}``.
#: Equivalent to :py:class:`~typing.Pattern`.
REGEX = enum.auto()
#: Converted to ``{"type": "object", ...}``.
#: Use this for arbitrary free-form objects (usually a :py:class:`dict`).
#: The ``additionalProperties`` item is added depending on the ``GENERIC_ADDITIONAL_PROPERTIES`` setting.
Expand All @@ -84,7 +106,7 @@ class OpenApiTypes(enum.Enum):
#: This signals that the request or response is empty.
NONE = enum.auto()
#: Converted to ``{}`` which sets no type and format.
#: Equivalent to :py:class:`typing.Any`.
#: Equivalent to :py:class:`~typing.Any`.
ANY = enum.auto()


Expand All @@ -103,15 +125,24 @@ class OpenApiTypes(enum.Enum):
OpenApiTypes.INT64: {'type': 'integer', 'format': 'int64'},
OpenApiTypes.UUID: {'type': 'string', 'format': 'uuid'},
OpenApiTypes.URI: {'type': 'string', 'format': 'uri'},
OpenApiTypes.URI_REF: {'type': 'string', 'format': 'uri-reference'},
OpenApiTypes.URI_TPL: {'type': 'string', 'format': 'uri-template'},
OpenApiTypes.IRI: {'type': 'string', 'format': 'iri'},
OpenApiTypes.IRI_REF: {'type': 'string', 'format': 'iri-reference'},
OpenApiTypes.IP4: {'type': 'string', 'format': 'ipv4'},
OpenApiTypes.IP6: {'type': 'string', 'format': 'ipv6'},
OpenApiTypes.HOSTNAME: {'type': 'string', 'format': 'hostname'},
OpenApiTypes.IDN_HOSTNAME: {'type': 'string', 'format': 'idn-hostname'},
OpenApiTypes.DECIMAL: {'type': 'number', 'format': 'double'},
OpenApiTypes.DATETIME: {'type': 'string', 'format': 'date-time'},
OpenApiTypes.DATE: {'type': 'string', 'format': 'date'},
OpenApiTypes.TIME: {'type': 'string', 'format': 'time'},
OpenApiTypes.DURATION: {'type': 'string', 'format': 'duration'}, # ISO 8601
OpenApiTypes.EMAIL: {'type': 'string', 'format': 'email'},
OpenApiTypes.IDN_EMAIL: {'type': 'string', 'format': 'idn-email'},
OpenApiTypes.JSON_PTR: {'type': 'string', 'format': 'json-pointer'},
OpenApiTypes.JSON_PTR_REL: {'type': 'string', 'format': 'relative-json-pointer'},
OpenApiTypes.REGEX: {'type': 'string', 'format': 'regex'},
OpenApiTypes.ANY: {},
OpenApiTypes.NONE: None,
# OpenApiTypes.OBJECT is inserted at runtime due to dependency on settings
Expand All @@ -127,8 +158,13 @@ class OpenApiTypes(enum.Enum):
Decimal: OpenApiTypes.DECIMAL,
datetime: OpenApiTypes.DATETIME,
date: OpenApiTypes.DATE,
time: OpenApiTypes.TIME,
timedelta: OpenApiTypes.DURATION,
IPv4Address: OpenApiTypes.IP4,
IPv6Address: OpenApiTypes.IP6,
Pattern: OpenApiTypes.REGEX,
dict: OpenApiTypes.OBJECT,
typing.Any: OpenApiTypes.ANY,
Any: OpenApiTypes.ANY,
None: OpenApiTypes.NONE,
}

Expand Down
2 changes: 1 addition & 1 deletion drf_spectacular/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ def get_tags(self):


def extend_schema_field(
field: Union[_SerializerType, Field, OpenApiTypes, Dict],
field: Union[_SerializerType, Field, _KnownPythonTypes, OpenApiTypes, Dict],
component_name: Optional[str] = None
) -> Callable[[F], F]:
"""
Expand Down