From 632f12fba48af1edbd92c722a09a5f7602315b3f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 11 Nov 2009 13:06:18 +0000 Subject: [PATCH] Fixed #12163 -- Corrected the unpickling of non-deferred models. Thanks to rfugger for the report and test case. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11732 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/base.py | 21 ++++++++++++++----- tests/regressiontests/defer_regress/models.py | 17 +++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index c7f6ba2f7c..47c177295d 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -361,6 +361,8 @@ class Model(object): defers = [] pk_val = None if self._deferred: + from django.db.models.query_utils import deferred_class_factory + factory = deferred_class_factory for field in self._meta.fields: if isinstance(self.__class__.__dict__.get(field.attname), DeferredAttribute): @@ -371,8 +373,9 @@ class Model(object): # once. obj = self.__class__.__dict__[field.attname] model = obj.model_ref() - - return (model_unpickle, (model, defers), data) + else: + factory = simple_class_factory + return (model_unpickle, (model, defers, factory), data) def _get_pk_val(self, meta=None): if not meta: @@ -657,12 +660,20 @@ def get_absolute_url(opts, func, self, *args, **kwargs): class Empty(object): pass -def model_unpickle(model, attrs): +def simple_class_factory(model, attrs): + """Used to unpickle Models without deferred fields. + + We need to do this the hard way, rather than just using + the default __reduce__ implementation, because of a + __deepcopy__ problem in Python 2.4 + """ + return model + +def model_unpickle(model, attrs, factory): """ Used to unpickle Model subclasses with deferred fields. """ - from django.db.models.query_utils import deferred_class_factory - cls = deferred_class_factory(model, attrs) + cls = factory(model, attrs) return cls.__new__(cls) model_unpickle.__safe_for_unpickle__ = True diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index da9822ab88..a1cd19788d 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -115,6 +115,23 @@ u'c1' >>> results[0].second_child.name u'c2' +# Test for #12163 - Pickling error saving session with unsaved model instances. +>>> from django.contrib.sessions.backends.db import SessionStore +>>> SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead' +>>> item = Item() +>>> item._deferred +False +>>> s = SessionStore(SESSION_KEY) +>>> s.clear() +>>> s['item'] = item +>>> s.save() +>>> s = SessionStore(SESSION_KEY) +>>> s.modified = True +>>> s.save() +>>> i2 = s['item'] +>>> i2._deferred # Item must still be non-deferred +False + # Finally, we need to flush the app cache for the defer module. # Using only/defer creates some artifical entries in the app cache # that messes up later tests. Purge all entries, just to be sure.