Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[1.5.x] Fixed prefetch_related + pickle regressions

There were a couple of regressions related to field pickling. The
regressions were introduced by QuerySet._known_related_objects caching.

The regressions aren't present in master, the fix was likely in
f403653.

Fixed #20157, fixed #20257. Also made QuerySets with model=None
picklable.
  • Loading branch information...
commit bac187c0d8e829fb3ca2ca82965eabbcbcb6ddd5 1 parent 63cab03
@akaariai akaariai authored
View
18 django/db/models/query.py
@@ -68,11 +68,27 @@ def __getstate__(self):
"""
# Force the cache to be fully populated.
len(self)
-
obj_dict = self.__dict__.copy()
obj_dict['_iter'] = None
+ obj_dict['_known_related_objects'] = dict(
+ (field.name, val) for field, val in self._known_related_objects.items()
+ )
return obj_dict
+ def __setstate__(self, obj_dict):
+ model = obj_dict['model']
+ if model is None:
+ # if model is None, then self should be emptyqs and the related
+ # objects do not matter.
+ self._known_related_objects = {}
+ else:
+ opts = model._meta
+ self._known_related_objects = dict(
+ (opts.get_field(field.name if hasattr(field, 'name') else field), val)
+ for field, val in obj_dict['_known_related_objects'].items()
+ )
+ self.__dict__.update(obj_dict)
+
def __repr__(self):
data = list(self[:REPR_OUTPUT_SIZE + 1])
if len(data) > REPR_OUTPUT_SIZE:
View
17 django/db/models/sql/query.py
@@ -208,12 +208,17 @@ def __setstate__(self, obj_dict):
Unpickling support.
"""
# Rebuild list of field instances
- opts = obj_dict['model']._meta
- obj_dict['select_fields'] = [
- name is not None and opts.get_field(name) or None
- for name in obj_dict['select_fields']
- ]
-
+ model = obj_dict['model']
+ if model is None:
+ # if model is None the queryset should be emptyqs. So the
+ # select_fields do not matter.
+ obj_dict['select_fields'] = []
+ else:
+ opts = model._meta
+ obj_dict['select_fields'] = [
+ name is not None and opts.get_field(name) or None
+ for name in obj_dict['select_fields']
+ ]
self.__dict__.update(obj_dict)
def prepare(self):
View
13 tests/regressiontests/queryset_pickle/models.py
@@ -36,3 +36,16 @@ class Happening(models.Model):
number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
number3 = models.IntegerField(blank=True, default=Numbers.get_class_number)
number4 = models.IntegerField(blank=True, default=nn.get_member_number)
+
+class Person(models.Model):
+ name = models.CharField(max_length=200)
+
+class SocialProfile(models.Model):
+ person = models.ForeignKey(Person)
+ friends = models.ManyToManyField('self')
+
+class Post(models.Model):
+ post_date = models.DateTimeField(default=datetime.datetime.now)
+
+class Material(models.Model):
+ post = models.ForeignKey(Post, related_name='materials')
View
29 tests/regressiontests/queryset_pickle/tests.py
@@ -5,7 +5,8 @@
from django.test import TestCase
-from .models import Group, Event, Happening
+from .models import Group, Event, Happening, Person, Post
+from django.contrib.auth.models import AnonymousUser
class PickleabilityTestCase(TestCase):
@@ -46,3 +47,29 @@ def test_doesnotexist_exception(self):
# can't just use assertEqual(original, unpickled)
self.assertEqual(original.__class__, unpickled.__class__)
self.assertEqual(original.args, unpickled.args)
+
+ def test_pickle_m2m_prefetch_related(self):
+ bob = Person(name="Bob")
+ bob.save()
+ people = Person.objects.prefetch_related('socialprofile_set')
+ dumped = pickle.dumps(people)
+ people = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ people, [bob], lambda x: x)
+
+ def test_pickle_field_default_prefetch_related(self):
+ p1 = Post.objects.create()
+ posts = Post.objects.prefetch_related('materials')
+ dumped = pickle.dumps(posts)
+ posts = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ posts, [p1], lambda x: x)
+
+ def test_pickle_emptyqs(self):
+ u = AnonymousUser()
+ # Use AnonymousUser, as AnonymousUser.groups has qs.model = None
+ empty = u.groups.all()
+ dumped = pickle.dumps(empty)
+ empty = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ empty, [])

0 comments on commit bac187c

Please sign in to comment.
Something went wrong with that request. Please try again.