Add support for SerializerMethodField (#179)

Closes #137, #179.
This commit is contained in:
John Carter
2018-08-08 06:23:36 +12:00
committed by Cristi Vîjdea
parent 1dd7cfe043
commit 748b5d3c2f
12 changed files with 507 additions and 4 deletions
@@ -0,0 +1,69 @@
import datetime
import decimal
import uuid
from rest_framework import serializers
class Unknown(object):
pass
class MethodFieldExampleSerializer(serializers.Serializer):
"""
Implementation of SerializerMethodField using type hinting for Python >= 3.5
"""
hinted_bool = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a bool")
def get_hinted_bool(self, obj) -> bool:
return True
hinted_int = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be an integer")
def get_hinted_int(self, obj) -> int:
return 1
hinted_float = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a number")
def get_hinted_float(self, obj) -> float:
return 1.0
hinted_decimal = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a decimal")
def get_hinted_decimal(self, obj) -> decimal.Decimal:
return decimal.Decimal(1)
hinted_datetime = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a datetime")
def get_hinted_datetime(self, obj) -> datetime.datetime:
return datetime.datetime.now()
hinted_date = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a date")
def get_hinted_date(self, obj) -> datetime.date:
return datetime.date.today()
hinted_uuid = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a uuid")
def get_hinted_uuid(self, obj) -> uuid.UUID:
return uuid.uuid4()
hinted_unknown = serializers.SerializerMethodField(
help_text="type hint is unknown, so is expected to fallback to string")
def get_hinted_unknown(self, obj) -> Unknown:
return Unknown()
non_hinted_number = serializers.SerializerMethodField(
help_text="No hint on the method, so this is expected to fallback to string")
def get_non_hinted_number(self, obj):
return 1.0
@@ -0,0 +1,82 @@
import datetime
import decimal
import uuid
from rest_framework import serializers
from drf_yasg.utils import swagger_serializer_method
class Unknown(object):
pass
class MethodFieldExampleSerializer(serializers.Serializer):
"""
Fallback implementation of SerializerMethodField type hinting for Python < 3.5
`->` syntax isn't supported, instead decorate with a serializer that returns the same type
a bit of a hack, but it provides a cross-check between hinting and decorator functionality.
"""
hinted_bool = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a bool")
@swagger_serializer_method(serializer=serializers.BooleanField)
def get_hinted_bool(self, obj):
return True
hinted_int = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be an integer")
@swagger_serializer_method(serializer=serializers.IntegerField)
def get_hinted_int(self, obj):
return 1
hinted_float = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a number")
@swagger_serializer_method(serializer=serializers.FloatField)
def get_hinted_float(self, obj):
return 1.0
hinted_decimal = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a decimal")
# note that in this case an instance is required since DecimalField has required arguments
@swagger_serializer_method(serializer=serializers.DecimalField(max_digits=6, decimal_places=4))
def get_hinted_decimal(self, obj):
return decimal.Decimal(1)
hinted_datetime = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a datetime")
@swagger_serializer_method(serializer=serializers.DateTimeField)
def get_hinted_datetime(self, obj):
return datetime.datetime.now()
hinted_date = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a date")
@swagger_serializer_method(serializer=serializers.DateField)
def get_hinted_date(self, obj):
return datetime.date.today()
hinted_uuid = serializers.SerializerMethodField(
help_text="the type hint on the method should determine this to be a uuid")
@swagger_serializer_method(serializer=serializers.UUIDField)
def get_hinted_uuid(self, obj):
return uuid.uuid4()
hinted_unknown = serializers.SerializerMethodField(
help_text="type hint is unknown, so is expected to fallback to string")
def get_hinted_unknown(self, obj):
return Unknown()
non_hinted_number = serializers.SerializerMethodField(
help_text="No hint on the method, so this is expected to fallback to string")
def get_non_hinted_number(self, obj):
return 1.0
+63 -1
View File
@@ -1,8 +1,19 @@
from django.contrib.auth.models import User
from rest_framework import serializers
from drf_yasg.utils import swagger_serializer_method
from snippets.models import Snippet
try:
import typing
from .method_serializers_with_typing import MethodFieldExampleSerializer
except ImportError:
from .method_serializers_without_typing import MethodFieldExampleSerializer
class OtherStuffSerializer(serializers.Serializer):
foo = serializers.CharField()
class UserSerializerrr(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
@@ -10,10 +21,61 @@ class UserSerializerrr(serializers.ModelSerializer):
last_connected_ip = serializers.IPAddressField(help_text="i'm out of ideas", protocol='ipv4', read_only=True)
last_connected_at = serializers.DateField(help_text="really?", read_only=True)
other_stuff = serializers.SerializerMethodField(
help_text="the decorator should determine the serializer class for this")
hint_example = MethodFieldExampleSerializer()
@swagger_serializer_method(serializer=OtherStuffSerializer)
def get_other_stuff(self, obj):
"""
method_field that uses a serializer internally.
By using the decorator, we can tell drf-yasg how to represent this in Swagger
:param obj:
:return:
"""
return OtherStuffSerializer().data
help_text_example_1 = serializers.SerializerMethodField(
help_text="help text on field is set, so this should appear in swagger"
)
@swagger_serializer_method(serializer=serializers.IntegerField(
help_text="decorated instance help_text shouldn't appear in swagger because field has priority"))
def get_help_text_example_1(self):
"""
method docstring shouldn't appear in swagger because field has priority
:return:
"""
return 1
help_text_example_2 = serializers.SerializerMethodField()
@swagger_serializer_method(serializer=serializers.IntegerField(
help_text="instance help_text is set, so should appear in swagger"))
def get_help_text_example_2(self):
"""
method docstring shouldn't appear in swagger because decorator has priority
:return:
"""
return 1
help_text_example_3 = serializers.SerializerMethodField()
@swagger_serializer_method(serializer=serializers.IntegerField())
def get_help_text_example_3(self):
"""
docstring is set so should appear in swagger as fallback
:return:
"""
return 1
class Meta:
model = User
fields = ('id', 'username', 'email', 'articles', 'snippets',
'last_connected_ip', 'last_connected_at', 'article_slugs')
'last_connected_ip', 'last_connected_at', 'article_slugs', 'other_stuff', 'hint_example',
'help_text_example_1', 'help_text_example_2', 'help_text_example_3')
class UserListQuerySerializer(serializers.Serializer):