Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 5510)
+++ django/db/models/base.py	(working copy)
@@ -94,9 +94,19 @@
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    _dirty = {}
+
+    def __setattr__(self, name, value):
+        if name != '_dirty' and value != getattr(self, name):
+            self._dirty[name] = True
+        super(Model, self).__setattr__(name, value)
+
+    def _wash(self):
+        self._dirty = {}
+
     def __init__(self, *args, **kwargs):
         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
-        
+
         # There is a rather weird disparity here; if kwargs, it's set, then args
         # overrides it. It should be one or the other; don't duplicate the work 
         # The reason for the kwargs check is that standard iterator passes in by
@@ -164,6 +174,8 @@
                     pass
             if kwargs:
                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
+
+        self._wash()
         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
 
     def add_to_class(cls, name, value):
@@ -201,7 +213,8 @@
     def save(self):
         dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
 
-        non_pks = [f for f in self._meta.fields if not f.primary_key]
+        non_pks = [f for f in self._meta.fields if not f.primary_key and (self._dirty.get(f.name, False) or self._dirty.get(f.attname, False))]
+        self._wash()
         cursor = connection.cursor()
 
         # First, try an UPDATE. If that doesn't update anything, do an INSERT.
