Allow serializer data to include related objects by object

When instantiating a serializer from cleaned data, the relational fields
have been already converted into objects, which makes the fields
invalid. Having a valid object as the value of a field is not
necessarily considered valid, if the object is not in the given
queryset.

As such, this change adds a check to allow objects of the model type and
in the queryset in lieu of the proper representation.
This commit is contained in:
Étienne Beaulé 2020-03-05 20:13:15 -05:00
parent 4a98533746
commit 4e561a4ac0
No known key found for this signature in database
GPG Key ID: E29CABE72BECCB8E
4 changed files with 29 additions and 1 deletions

View File

@ -250,7 +250,9 @@ class PrimaryKeyRelatedField(RelatedField):
return True
def to_internal_value(self, data):
if self.pk_field is not None:
if isinstance(data, self.get_queryset().model):
data = data.pk
elif self.pk_field is not None:
data = self.pk_field.to_internal_value(data)
try:
return self.get_queryset().get(pk=data)
@ -331,6 +333,12 @@ class HyperlinkedRelatedField(RelatedField):
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
def to_internal_value(self, data):
if isinstance(data, self.get_queryset().model):
try:
return self.get_queryset().get(**{self.lookup_url_kwarg: getattr(data, self.lookup_field)})
except (ObjectDoesNotExist, ObjectValueError, ObjectTypeError):
self.fail('does_not_exist')
request = self.context.get('request', None)
try:
http_prefix = data.startswith(('http:', 'https:'))

View File

@ -116,6 +116,10 @@ class TestPrimaryKeyRelatedField(APISimpleTestCase):
instance = field.to_internal_value(self.instance.pk)
assert instance is self.instance
def test_pk_related_object_given(self):
instance = self.field.to_internal_value(self.instance)
assert instance is self.queryset.items[2]
class TestProxiedPrimaryKeyRelatedField(APISimpleTestCase):
def setUp(self):
@ -216,6 +220,11 @@ class TestHyperlinkedRelatedField(APISimpleTestCase):
def hyperlinked_related_queryset_error(self, exc_type):
class QuerySet:
class FakeObject:
pass
model = FakeObject
def get(self, *args, **kwargs):
raise exc_type
@ -235,6 +244,10 @@ class TestHyperlinkedRelatedField(APISimpleTestCase):
def test_hyperlinked_related_queryset_value_error(self):
self.hyperlinked_related_queryset_error(ValueError)
def test_hyperlinked_related_object_given(self):
instance = self.field.to_internal_value(self.queryset.items[2])
assert instance is self.queryset.items[2]
class TestHyperlinkedIdentityField(APISimpleTestCase):
def setUp(self):

View File

@ -322,6 +322,11 @@ class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase):
super().setUp()
class MockQueryset:
class MockObject:
pass
model = MockObject
def get(self, pk):
return 'object %s' % pk

View File

@ -17,6 +17,8 @@ class MockObject:
class MockQueryset:
model = MockObject
def __init__(self, iterable):
self.items = iterable