Refs #28575 -- Made RelatedObjectDoesNotExist classes pickable.
Thanks to Rachel Tobin for the initial __qualname__ work and tests.
This commit is contained in:
parent
4a861e8850
commit
d4fb742094
|
@ -92,8 +92,13 @@ class ForwardManyToOneDescriptor:
|
||||||
# still be a string model reference.
|
# still be a string model reference.
|
||||||
return type(
|
return type(
|
||||||
'RelatedObjectDoesNotExist',
|
'RelatedObjectDoesNotExist',
|
||||||
(self.field.remote_field.model.DoesNotExist, AttributeError),
|
(self.field.remote_field.model.DoesNotExist, AttributeError), {
|
||||||
{}
|
'__module__': self.field.model.__module__,
|
||||||
|
'__qualname__': '%s.%s.RelatedObjectDoesNotExist' % (
|
||||||
|
self.field.model.__qualname__,
|
||||||
|
self.field.name,
|
||||||
|
),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_cached(self, instance):
|
def is_cached(self, instance):
|
||||||
|
@ -244,6 +249,14 @@ class ForwardManyToOneDescriptor:
|
||||||
if value is not None and not remote_field.multiple:
|
if value is not None and not remote_field.multiple:
|
||||||
remote_field.set_cached_value(value, instance)
|
remote_field.set_cached_value(value, instance)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
"""
|
||||||
|
Pickling should return the instance attached by self.field on the
|
||||||
|
model, not a new copy of that descriptor. Use getattr() to retrieve
|
||||||
|
the instance directly from the model.
|
||||||
|
"""
|
||||||
|
return getattr, (self.field.model, self.field.name)
|
||||||
|
|
||||||
|
|
||||||
class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor):
|
class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor):
|
||||||
"""
|
"""
|
||||||
|
@ -317,8 +330,13 @@ class ReverseOneToOneDescriptor:
|
||||||
# consistency with `ForwardManyToOneDescriptor`.
|
# consistency with `ForwardManyToOneDescriptor`.
|
||||||
return type(
|
return type(
|
||||||
'RelatedObjectDoesNotExist',
|
'RelatedObjectDoesNotExist',
|
||||||
(self.related.related_model.DoesNotExist, AttributeError),
|
(self.related.related_model.DoesNotExist, AttributeError), {
|
||||||
{}
|
'__module__': self.related.model.__module__,
|
||||||
|
'__qualname__': '%s.%s.RelatedObjectDoesNotExist' % (
|
||||||
|
self.related.model.__qualname__,
|
||||||
|
self.related.name,
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_cached(self, instance):
|
def is_cached(self, instance):
|
||||||
|
@ -455,6 +473,10 @@ class ReverseOneToOneDescriptor:
|
||||||
# instance to avoid an extra SQL query if it's accessed later on.
|
# instance to avoid an extra SQL query if it's accessed later on.
|
||||||
self.related.field.set_cached_value(value, instance)
|
self.related.field.set_cached_value(value, instance)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
# Same purpose as ForwardManyToOneDescriptor.__reduce__().
|
||||||
|
return getattr, (self.related.model, self.related.name)
|
||||||
|
|
||||||
|
|
||||||
class ReverseManyToOneDescriptor:
|
class ReverseManyToOneDescriptor:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -45,6 +45,7 @@ class Happening(models.Model):
|
||||||
name = models.CharField(blank=True, max_length=100, default="test")
|
name = models.CharField(blank=True, max_length=100, default="test")
|
||||||
number1 = models.IntegerField(blank=True, default=standalone_number)
|
number1 = models.IntegerField(blank=True, default=standalone_number)
|
||||||
number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
|
number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
|
||||||
|
event = models.OneToOneField(Event, models.CASCADE, null=True)
|
||||||
|
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
|
|
|
@ -55,6 +55,18 @@ class PickleabilityTestCase(TestCase):
|
||||||
klass = Event.MultipleObjectsReturned
|
klass = Event.MultipleObjectsReturned
|
||||||
self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
|
self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
|
||||||
|
|
||||||
|
def test_forward_relatedobjectdoesnotexist_class(self):
|
||||||
|
# ForwardManyToOneDescriptor
|
||||||
|
klass = Event.group.RelatedObjectDoesNotExist
|
||||||
|
self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
|
||||||
|
# ForwardOneToOneDescriptor
|
||||||
|
klass = Happening.event.RelatedObjectDoesNotExist
|
||||||
|
self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
|
||||||
|
|
||||||
|
def test_reverse_one_to_one_relatedobjectdoesnotexist_class(self):
|
||||||
|
klass = Event.happening.RelatedObjectDoesNotExist
|
||||||
|
self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
|
||||||
|
|
||||||
def test_manager_pickle(self):
|
def test_manager_pickle(self):
|
||||||
pickle.loads(pickle.dumps(Happening.objects))
|
pickle.loads(pickle.dumps(Happening.objects))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue