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.