mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
Fixes #6751 - ModelSerializer fields does not get updated correctly when signals are connected to some fields (#6752)
* fixes #6751 * reverted condition * save instance before setting m2m fields * added comment why m2m fields are saved after instance * removed blank line * added test for the issue 6751
This commit is contained in:
parent
72de94a05d
commit
6a95451d72
|
@ -973,14 +973,22 @@ class ModelSerializer(Serializer):
|
||||||
# Note that unlike `.create()` we don't need to treat many-to-many
|
# Note that unlike `.create()` we don't need to treat many-to-many
|
||||||
# relationships as being a special case. During updates we already
|
# relationships as being a special case. During updates we already
|
||||||
# have an instance pk for the relationships to be associated with.
|
# have an instance pk for the relationships to be associated with.
|
||||||
|
m2m_fields = []
|
||||||
for attr, value in validated_data.items():
|
for attr, value in validated_data.items():
|
||||||
if attr in info.relations and info.relations[attr].to_many:
|
if attr in info.relations and info.relations[attr].to_many:
|
||||||
field = getattr(instance, attr)
|
m2m_fields.append((attr, value))
|
||||||
field.set(value)
|
|
||||||
else:
|
else:
|
||||||
setattr(instance, attr, value)
|
setattr(instance, attr, value)
|
||||||
|
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
|
# Note that many-to-many fields are set after updating instance.
|
||||||
|
# Setting m2m fields triggers signals which could potentialy change
|
||||||
|
# updated instance and we do not want it to collide with .update()
|
||||||
|
for attr, value in m2m_fields:
|
||||||
|
field = getattr(instance, attr)
|
||||||
|
field.set(value)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
# Determine the fields to apply...
|
# Determine the fields to apply...
|
||||||
|
|
|
@ -18,6 +18,8 @@ from django.core.validators import (
|
||||||
MaxValueValidator, MinLengthValidator, MinValueValidator
|
MaxValueValidator, MinLengthValidator, MinValueValidator
|
||||||
)
|
)
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models.signals import m2m_changed
|
||||||
|
from django.dispatch import receiver
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -1251,7 +1253,6 @@ class Issue6110ModelSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class Issue6110Test(TestCase):
|
class Issue6110Test(TestCase):
|
||||||
|
|
||||||
def test_model_serializer_custom_manager(self):
|
def test_model_serializer_custom_manager(self):
|
||||||
instance = Issue6110ModelSerializer().create({'name': 'test_name'})
|
instance = Issue6110ModelSerializer().create({'name': 'test_name'})
|
||||||
self.assertEqual(instance.name, 'test_name')
|
self.assertEqual(instance.name, 'test_name')
|
||||||
|
@ -1260,3 +1261,43 @@ class Issue6110Test(TestCase):
|
||||||
msginitial = ('Got a `TypeError` when calling `Issue6110TestModel.all_objects.create()`.')
|
msginitial = ('Got a `TypeError` when calling `Issue6110TestModel.all_objects.create()`.')
|
||||||
with self.assertRaisesMessage(TypeError, msginitial):
|
with self.assertRaisesMessage(TypeError, msginitial):
|
||||||
Issue6110ModelSerializer().create({'wrong_param': 'wrong_param'})
|
Issue6110ModelSerializer().create({'wrong_param': 'wrong_param'})
|
||||||
|
|
||||||
|
|
||||||
|
class Issue6751Model(models.Model):
|
||||||
|
many_to_many = models.ManyToManyField(ManyToManyTargetModel, related_name='+')
|
||||||
|
char_field = models.CharField(max_length=100)
|
||||||
|
char_field2 = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(m2m_changed, sender=Issue6751Model.many_to_many.through)
|
||||||
|
def process_issue6751model_m2m_changed(action, instance, **_):
|
||||||
|
if action == 'post_add':
|
||||||
|
instance.char_field = 'value changed by signal'
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Issue6751Test(TestCase):
|
||||||
|
def test_model_serializer_save_m2m_after_instance(self):
|
||||||
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Issue6751Model
|
||||||
|
fields = (
|
||||||
|
'many_to_many',
|
||||||
|
'char_field',
|
||||||
|
)
|
||||||
|
|
||||||
|
instance = Issue6751Model.objects.create(char_field='initial value')
|
||||||
|
m2m_target = ManyToManyTargetModel.objects.create(name='target')
|
||||||
|
|
||||||
|
serializer = TestSerializer(
|
||||||
|
instance=instance,
|
||||||
|
data={
|
||||||
|
'many_to_many': (m2m_target.id,),
|
||||||
|
'char_field': 'will be changed by signal',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
serializer.is_valid()
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
self.assertEqual(instance.char_field, 'value changed by signal')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user