mirror of https://github.com/django/django.git
Fix #17879: Corrected regression in python (inherited by yaml and json) serializer that prevented serializing model instances with null FK ref to a model when serializing with natural keys. Thanks danfairs and tmitchell.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17685 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2f520139be
commit
20c69c5e51
1
AUTHORS
1
AUTHORS
|
@ -369,6 +369,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Slawek Mikula <slawek dot mikula at gmail dot com>
|
Slawek Mikula <slawek dot mikula at gmail dot com>
|
||||||
Shawn Milochik <shawn@milochik.com>
|
Shawn Milochik <shawn@milochik.com>
|
||||||
mitakummaa@gmail.com
|
mitakummaa@gmail.com
|
||||||
|
Taylor Mitchell <taylor.mitchell@gmail.com>
|
||||||
mmarshall
|
mmarshall
|
||||||
Andreas Mock <andreas.mock@web.de>
|
Andreas Mock <andreas.mock@web.de>
|
||||||
Reza Mohammadi <reza@zeerak.ir>
|
Reza Mohammadi <reza@zeerak.ir>
|
||||||
|
|
|
@ -47,7 +47,10 @@ class Serializer(base.Serializer):
|
||||||
def handle_fk_field(self, obj, field):
|
def handle_fk_field(self, obj, field):
|
||||||
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
|
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
|
||||||
related = getattr(obj, field.name)
|
related = getattr(obj, field.name)
|
||||||
value = related.natural_key()
|
if related:
|
||||||
|
value = related.natural_key()
|
||||||
|
else:
|
||||||
|
value = None
|
||||||
else:
|
else:
|
||||||
value = getattr(obj, field.get_attname())
|
value = getattr(obj, field.get_attname())
|
||||||
self._current[field.name] = value
|
self._current[field.name] = value
|
||||||
|
|
|
@ -111,6 +111,18 @@ class Anchor(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('id',)
|
ordering = ('id',)
|
||||||
|
|
||||||
|
class NaturalKeyAnchorManager(models.Manager):
|
||||||
|
def get_by_natural_key(self, data):
|
||||||
|
return self.get(data=data)
|
||||||
|
|
||||||
|
class NaturalKeyAnchor(models.Model):
|
||||||
|
objects = NaturalKeyAnchorManager()
|
||||||
|
|
||||||
|
data = models.CharField(max_length=100, unique=True)
|
||||||
|
|
||||||
|
def natural_key(self):
|
||||||
|
return (self.data,)
|
||||||
|
|
||||||
class UniqueAnchor(models.Model):
|
class UniqueAnchor(models.Model):
|
||||||
"""This is a model that can be used as
|
"""This is a model that can be used as
|
||||||
something for other models to point at"""
|
something for other models to point at"""
|
||||||
|
@ -120,6 +132,9 @@ class UniqueAnchor(models.Model):
|
||||||
class FKData(models.Model):
|
class FKData(models.Model):
|
||||||
data = models.ForeignKey(Anchor, null=True)
|
data = models.ForeignKey(Anchor, null=True)
|
||||||
|
|
||||||
|
class FKDataNaturalKey(models.Model):
|
||||||
|
data = models.ForeignKey(NaturalKeyAnchor, null=True)
|
||||||
|
|
||||||
class M2MData(models.Model):
|
class M2MData(models.Model):
|
||||||
data = models.ManyToManyField(Anchor, null=True)
|
data = models.ManyToManyField(Anchor, null=True)
|
||||||
|
|
||||||
|
@ -272,3 +287,4 @@ class LengthModel(models.Model):
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ from .models import (BooleanData, CharData, DateData, DateTimeData, EmailData,
|
||||||
PositiveSmallIntegerPKData, SlugPKData, SmallPKData, USStatePKData,
|
PositiveSmallIntegerPKData, SlugPKData, SmallPKData, USStatePKData,
|
||||||
AutoNowDateTimeData, ModifyingSaveData, InheritAbstractModel, BaseModel,
|
AutoNowDateTimeData, ModifyingSaveData, InheritAbstractModel, BaseModel,
|
||||||
ExplicitInheritBaseModel, InheritBaseModel, ProxyBaseModel,
|
ExplicitInheritBaseModel, InheritBaseModel, ProxyBaseModel,
|
||||||
ProxyProxyBaseModel, BigIntegerData, LengthModel, Tag, ComplexModel)
|
ProxyProxyBaseModel, BigIntegerData, LengthModel, Tag, ComplexModel,
|
||||||
|
NaturalKeyAnchor, FKDataNaturalKey)
|
||||||
|
|
||||||
# A set of functions that can be used to recreate
|
# A set of functions that can be used to recreate
|
||||||
# test data objects of various kinds.
|
# test data objects of various kinds.
|
||||||
|
@ -353,6 +354,12 @@ The end."""),
|
||||||
(data_obj, 1005, LengthModel, 1),
|
(data_obj, 1005, LengthModel, 1),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
natural_key_test_data = [
|
||||||
|
(data_obj, 1100, NaturalKeyAnchor, "Natural Key Anghor"),
|
||||||
|
(fk_obj, 1101, FKDataNaturalKey, 1100),
|
||||||
|
(fk_obj, 1102, FKDataNaturalKey, None),
|
||||||
|
]
|
||||||
|
|
||||||
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
||||||
# when field.empty_strings_allowed is True and the value is None; skip these
|
# when field.empty_strings_allowed is True and the value is None; skip these
|
||||||
# tests.
|
# tests.
|
||||||
|
@ -452,6 +459,35 @@ def serializerTest(format, self):
|
||||||
for klass, count in instance_count.items():
|
for klass, count in instance_count.items():
|
||||||
self.assertEqual(count, klass.objects.count())
|
self.assertEqual(count, klass.objects.count())
|
||||||
|
|
||||||
|
def naturalKeySerializerTest(format, self):
|
||||||
|
# Create all the objects defined in the test data
|
||||||
|
objects = []
|
||||||
|
instance_count = {}
|
||||||
|
for (func, pk, klass, datum) in natural_key_test_data:
|
||||||
|
with connection.constraint_checks_disabled():
|
||||||
|
objects.extend(func[0](pk, klass, datum))
|
||||||
|
|
||||||
|
# Get a count of the number of objects created for each class
|
||||||
|
for klass in instance_count:
|
||||||
|
instance_count[klass] = klass.objects.count()
|
||||||
|
|
||||||
|
# Serialize the test database
|
||||||
|
serialized_data = serializers.serialize(format, objects, indent=2,
|
||||||
|
use_natural_keys=True)
|
||||||
|
|
||||||
|
for obj in serializers.deserialize(format, serialized_data):
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
# Assert that the deserialized data is the same
|
||||||
|
# as the original source
|
||||||
|
for (func, pk, klass, datum) in natural_key_test_data:
|
||||||
|
func[1](self, pk, klass, datum)
|
||||||
|
|
||||||
|
# Assert that the number of objects deserialized is the
|
||||||
|
# same as the number that was serialized.
|
||||||
|
for klass, count in instance_count.items():
|
||||||
|
self.assertEqual(count, klass.objects.count())
|
||||||
|
|
||||||
def fieldsTest(format, self):
|
def fieldsTest(format, self):
|
||||||
obj = ComplexModel(field1='first', field2='second', field3='third')
|
obj = ComplexModel(field1='first', field2='second', field3='third')
|
||||||
obj.save_base(raw=True)
|
obj.save_base(raw=True)
|
||||||
|
@ -482,6 +518,8 @@ def streamTest(format, self):
|
||||||
|
|
||||||
for format in serializers.get_serializer_formats():
|
for format in serializers.get_serializer_formats():
|
||||||
setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
|
setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
|
||||||
|
setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format))
|
||||||
setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
|
setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
|
||||||
if format != 'python':
|
if format != 'python':
|
||||||
setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
|
setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue