Skip to content

Commit

Permalink
Json field support (#20)
Browse files Browse the repository at this point in the history
* Add JSONField support

* Test for deep inheritance
  • Loading branch information
djbios authored Feb 8, 2024
1 parent c3f0611 commit a147403
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 19 deletions.
43 changes: 24 additions & 19 deletions src/drf_pydantic/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import pydantic.fields
import pydantic_core

from pydantic import JsonValue
from pydantic._internal._fields import PydanticMetadata
from rest_framework import serializers # type: ignore

Expand Down Expand Up @@ -44,6 +45,7 @@
datetime.date: serializers.DateField,
datetime.time: serializers.TimeField,
datetime.timedelta: serializers.DurationField,
JsonValue: serializers.JSONField,
}


Expand Down Expand Up @@ -243,15 +245,14 @@ def _convert_type( # noqa: PLR0911

# Scalar field
if is_scalar(type_):
# Nested model
if issubclass(type_, pydantic.BaseModel):
try:
return getattr(type_, "drf_serializer")(**kwargs)
except AttributeError:
return create_serializer_from_model(type_)(**kwargs)

# Normal class
if inspect.isclass(type_):
# Nested model
if issubclass(type_, pydantic.BaseModel):
try:
return getattr(type_, "drf_serializer")(**kwargs)
except AttributeError:
return create_serializer_from_model(type_)(**kwargs)

# Decimal
if type_ is decimal.Decimal:
_context = decimal.getcontext()
Expand Down Expand Up @@ -279,18 +280,22 @@ def _convert_type( # noqa: PLR0911
)
elif len(regex_patterns) == 1:
return serializers.RegexField(regex=item.pattern, **kwargs)
else:
for key in [type_, type_.__base__]:
try:
return FIELD_MAP[key](**kwargs)
except KeyError:
continue
# Enum
elif issubclass(type_, enum.Enum):
return serializers.ChoiceField(
choices=[item.value for item in type_], **kwargs
)

# Enum
if issubclass(type_, enum.Enum):
return serializers.ChoiceField(
choices=[item.value for item in type_], **kwargs
)
# Explicitly defined field
class_candidates = [type_]
if hasattr(type_, "__mro__"):
class_candidates.extend(type_.__mro__)

for key in class_candidates:
try:
return FIELD_MAP[key](**kwargs)
except KeyError:
continue

raise FieldConversionError(f"{type_.__name__} is not a supported scalar type")

Expand Down
24 changes: 24 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from drf_pydantic import BaseModel
from drf_pydantic.errors import ModelConversionError
from pydantic import ConfigDict, JsonValue
from rest_framework import serializers


Expand Down Expand Up @@ -277,6 +278,14 @@ class Person(BaseModel):
assert "Error when converting model: Person" in str(exc_info.value)
assert "Field has multiple max_digits or decimal_places" in str(exc_info.value)

def test_json(self):
class Person(BaseModel):
data: JsonValue

serializer = Person.drf_serializer()

assert isinstance(serializer.fields["data"], serializers.JSONField)

def test_datetime(self):
class Person(BaseModel):
created_at: datetime.datetime
Expand Down Expand Up @@ -322,6 +331,21 @@ class Person(BaseModel):
assert isinstance(serializer.fields["gender"], serializers.ChoiceField)
assert serializer.fields["gender"].choices == {0: 0, 1: 1}

def test_deeply_inherited_types(self):
class CustomStr(str):
pass

class DeepCustomStr(CustomStr):
pass

class Person(BaseModel):
name: DeepCustomStr
model_config = ConfigDict(arbitrary_types_allowed=True)

serializer = Person.drf_serializer()

assert isinstance(serializer.fields["name"], serializers.CharField)

def test_literal(self):
class Employee(BaseModel):
department: typing.Literal["engineering", "sales", "marketing"]
Expand Down

0 comments on commit a147403

Please sign in to comment.