diff --git a/django/db/models/query.py b/django/db/models/query.py index 292769303d6..95654ee845d 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -68,11 +68,27 @@ class QuerySet(object): """ # 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: diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 7368fed24c9..390444d7990 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -208,12 +208,17 @@ class Query(object): 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): diff --git a/tests/regressiontests/queryset_pickle/models.py b/tests/regressiontests/queryset_pickle/models.py index d3500c903be..871fdd8a110 100644 --- a/tests/regressiontests/queryset_pickle/models.py +++ b/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') diff --git a/tests/regressiontests/queryset_pickle/tests.py b/tests/regressiontests/queryset_pickle/tests.py index ab32e8f6479..19519b2db99 100644 --- a/tests/regressiontests/queryset_pickle/tests.py +++ b/tests/regressiontests/queryset_pickle/tests.py @@ -5,7 +5,8 @@ import datetime 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 @@ class PickleabilityTestCase(TestCase): # 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, [])