Skip to content

Commit

Permalink
Merge pull request #17 from Saritasa/feature/search-instance-by-uuid
Browse files Browse the repository at this point in the history
Fix problem for update models with UUID id fields
  • Loading branch information
ruscoder authored Aug 16, 2017
2 parents 736a648 + 9039d09 commit 8f97cf2
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## 0.2.1
* Fix problem for updating models with UUID primary key field (@kseniyashaydurova)
* Fix problem with raising Protected Error in deletion (@kseniyashaydurova)

## 0.2.0
Expand Down
23 changes: 17 additions & 6 deletions drf_writable_nested/mixins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import uuid
from collections import OrderedDict, defaultdict

from django.contrib.contenttypes.fields import GenericRelation
Expand Down Expand Up @@ -71,7 +72,8 @@ def _get_serializer_for_field(self, field, **kwargs):

def _get_generic_lookup(self, instance, related_field):
return {
related_field.content_type_field_name: ContentType.objects.get_for_model(instance),
related_field.content_type_field_name:
ContentType.objects.get_for_model(instance),
related_field.object_id_field_name: instance.pk,
}

Expand All @@ -82,17 +84,23 @@ def prefetch_related_instances(self, field, related_data):
pk = self._get_related_pk(d, model_class)
if pk:
pk_list.append(pk)

instances = {
related_instance.pk: related_instance
str(related_instance.pk): related_instance
for related_instance in model_class.objects.filter(
pk__in=pk_list,
pk__in=pk_list
)
}

return instances

def _get_related_pk(self, data, model_class):
return data.get('pk') or \
data.get(model_class._meta.pk.attname)
pk = data.get('pk') or data.get(model_class._meta.pk.attname)

if pk:
return str(pk)

return None

def update_or_create_reverse_relations(self, instance, reverse_relations):
# Update or create reverse relations:
Expand Down Expand Up @@ -158,6 +166,7 @@ def update_or_create_direct_relations(self, attrs, relations):

def save(self, **kwargs):
self.save_kwargs = defaultdict(dict, kwargs)

return super(BaseNestedModelSerializer, self).save(**kwargs)

def get_save_kwargs(self, field_name):
Expand All @@ -166,6 +175,7 @@ def get_save_kwargs(self, field_name):
raise TypeError(
_("Arguments to nested serializer's `save` must be dict's")
)

return save_kwargs


Expand Down Expand Up @@ -240,7 +250,8 @@ def delete_reverse_relations_if_need(self, instance, reverse_relations):
related_field.rel.name: instance,
}
elif isinstance(related_field, GenericRelation):
related_field_lookup = self._get_generic_lookup(instance, related_field)
related_field_lookup = \
self._get_generic_lookup(instance, related_field)
else:
related_field_lookup = {
related_field.name: instance,
Expand Down
11 changes: 11 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import uuid
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
Expand Down Expand Up @@ -59,3 +60,13 @@ class CustomPK(models.Model):
on_delete=models.CASCADE,
related_name='custompks',
)


class Message(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
profile = models.ForeignKey(Profile, related_name='messages')
message = models.CharField(max_length=100)
12 changes: 11 additions & 1 deletion tests/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ class Meta:
fields = ('pk', 'image',)


class MessageSerializer(serializers.ModelSerializer):

class Meta:
model = models.Message
fields = ('pk', 'message',)


class SiteSerializer(serializers.ModelSerializer):
url = serializers.CharField()

Expand All @@ -37,9 +44,12 @@ class ProfileSerializer(WritableNestedModelSerializer):
# Direct FK relation
access_key = AccessKeySerializer(allow_null=True)

# Reverse FK relation with UUID
messages = MessageSerializer(many=True)

class Meta:
model = models.Profile
fields = ('pk', 'sites', 'avatars', 'access_key',)
fields = ('pk', 'sites', 'avatars', 'access_key', 'messages',)


class UserSerializer(WritableNestedModelSerializer):
Expand Down
44 changes: 44 additions & 0 deletions tests/test_writable_nested_model_serializer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import uuid
from rest_framework.exceptions import ValidationError
from django.test import TestCase

Expand Down Expand Up @@ -31,6 +32,17 @@ def get_initial_data(self):
'image': 'image-2.png',
},
],
'messages': [
{
'message': 'Message 1'
},
{
'message': 'Message 2'
},
{
'message': 'Message 3'
},
]
},
}

Expand Down Expand Up @@ -111,11 +123,14 @@ def test_update(self):
self.assertEqual(models.Profile.objects.count(), 1)
self.assertEqual(models.Site.objects.count(), 2)
self.assertEqual(models.Avatar.objects.count(), 2)
self.assertEqual(models.Message.objects.count(), 3)

# Update
user_pk = user.pk
profile_pk = user.profile.pk

message_to_update_str_pk = str(user.profile.messages.first().pk)
message_to_update_pk = user.profile.messages.last().pk
serializer = serializers.UserSerializer(
instance=user,
data={
Expand All @@ -141,6 +156,19 @@ def test_update(self):
'image': 'new-image-2.png',
},
],
'messages': [
{
'pk': message_to_update_str_pk,
'message': 'Old message 1'
},
{
'pk': message_to_update_pk,
'message': 'Old message 2'
},
{
'message': 'New message 1'
}
],
},
},
)
Expand All @@ -166,12 +194,27 @@ def test_update(self):
set(profile.avatars.values_list('image', flat=True)),
{'old-image-1.png', 'new-image-1.png', 'new-image-2.png'}
)
self.assertSetEqual(
set(profile.messages.values_list('message', flat=True)),
{'Old message 1', 'Old message 2', 'New message 1'}
)
# Check that message which supposed to be updated still in profile
# messages (new message wasn't created instead of update)
self.assertIn(
message_to_update_pk,
profile.messages.values_list('id', flat=True)
)
self.assertIn(
uuid.UUID(message_to_update_str_pk),
profile.messages.values_list('id', flat=True)
)

# Check instances count
self.assertEqual(models.User.objects.count(), 1)
self.assertEqual(models.Profile.objects.count(), 1)
self.assertEqual(models.Site.objects.count(), 1)
self.assertEqual(models.Avatar.objects.count(), 3)
self.assertEqual(models.Message.objects.count(), 3)
# Access key shouldn't be removed because it is FK
self.assertEqual(models.AccessKey.objects.count(), 1)

Expand All @@ -197,6 +240,7 @@ def test_update_raise_protected_error(self):
'image': 'new-image-1.png',
},
],
'messages': [],
}
)

Expand Down

0 comments on commit 8f97cf2

Please sign in to comment.