Skip to content

Commit 0e83063

Browse files
committed
Merge pull request #3936 from carltongibson/null-uuid-fk-take2
Fix None UUID ForeignKey serialization
2 parents 85e57af + 2ef74cf commit 0e83063

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

docs/topics/release-notes.md

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ You can determine your currently installed version using `pip freeze`:
4545
**Unreleased**
4646

4747
* Dropped support for EOL Django 1.7 ([#3933][gh3933])
48+
* Fixed null foreign keys targeting UUIDField primary keys. ([#3936][gh3936])
4849

4950
### 3.3.2
5051

rest_framework/serializers.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,13 @@ def to_representation(self, instance):
464464
except SkipField:
465465
continue
466466

467-
if attribute is None:
468-
# We skip `to_representation` for `None` values so that
469-
# fields do not have to explicitly deal with that case.
467+
# We skip `to_representation` for `None` values so that fields do
468+
# not have to explicitly deal with that case.
469+
#
470+
# For related fields with `use_pk_only_optimization` we need to
471+
# resolve the pk value.
472+
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
473+
if check_for_none is None:
470474
ret[field.field_name] = None
471475
else:
472476
ret[field.field_name] = field.to_representation(attribute)

tests/models.py

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import unicode_literals
22

3+
import uuid
4+
35
from django.db import models
46
from django.utils.translation import ugettext_lazy as _
57

@@ -46,6 +48,11 @@ class ForeignKeyTarget(RESTFrameworkModel):
4648
name = models.CharField(max_length=100)
4749

4850

51+
class UUIDForeignKeyTarget(RESTFrameworkModel):
52+
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
53+
name = models.CharField(max_length=100)
54+
55+
4956
class ForeignKeySource(RESTFrameworkModel):
5057
name = models.CharField(max_length=100)
5158
target = models.ForeignKey(ForeignKeyTarget, related_name='sources',
@@ -62,6 +69,14 @@ class NullableForeignKeySource(RESTFrameworkModel):
6269
on_delete=models.CASCADE)
6370

6471

72+
class NullableUUIDForeignKeySource(RESTFrameworkModel):
73+
name = models.CharField(max_length=100)
74+
target = models.ForeignKey(ForeignKeyTarget, null=True, blank=True,
75+
related_name='nullable_sources',
76+
verbose_name='Optional target object',
77+
on_delete=models.CASCADE)
78+
79+
6580
# OneToOne
6681
class OneToOneTarget(RESTFrameworkModel):
6782
name = models.CharField(max_length=100)

tests/test_relations_pk.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from rest_framework import serializers
77
from tests.models import (
88
ForeignKeySource, ForeignKeyTarget, ManyToManySource, ManyToManyTarget,
9-
NullableForeignKeySource, NullableOneToOneSource, OneToOneTarget
9+
NullableForeignKeySource, NullableOneToOneSource,
10+
NullableUUIDForeignKeySource, OneToOneTarget, UUIDForeignKeyTarget
1011
)
1112

1213

@@ -43,6 +44,18 @@ class Meta:
4344
fields = ('id', 'name', 'target')
4445

4546

47+
# Nullable UUIDForeignKey
48+
class NullableUUIDForeignKeySourceSerializer(serializers.ModelSerializer):
49+
target = serializers.PrimaryKeyRelatedField(
50+
pk_field=serializers.UUIDField(),
51+
queryset=UUIDForeignKeyTarget.objects.all(),
52+
allow_null=True)
53+
54+
class Meta:
55+
model = NullableUUIDForeignKeySource
56+
fields = ('id', 'name', 'target')
57+
58+
4659
# Nullable OneToOne
4760
class NullableOneToOneTargetSerializer(serializers.ModelSerializer):
4861
class Meta:
@@ -432,6 +445,17 @@ def test_foreign_key_update_with_valid_emptystring(self):
432445
]
433446
self.assertEqual(serializer.data, expected)
434447

448+
def test_null_uuid_foreign_key_serializes_as_none(self):
449+
source = NullableUUIDForeignKeySource(name='Source')
450+
serializer = NullableUUIDForeignKeySourceSerializer(source)
451+
data = serializer.data
452+
self.assertEqual(data["target"], None)
453+
454+
def test_nullable_uuid_foreign_key_is_valid_when_none(self):
455+
data = {"name": "Source", "target": None}
456+
serializer = NullableUUIDForeignKeySourceSerializer(data=data)
457+
self.assertTrue(serializer.is_valid(), serializer.errors)
458+
435459

436460
class PKNullableOneToOneTests(TestCase):
437461
def setUp(self):

0 commit comments

Comments
 (0)