Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 5832)
+++ django/db/models/base.py	(working copy)
@@ -19,6 +19,11 @@
 import sys
 import os
 
+try:
+    set
+except NameError:
+    from sets import Set as set   # Python 2.3 fallback
+
 class ModelBase(type):
     "Metaclass for all models"
     def __new__(cls, name, bases, attrs):
@@ -97,13 +102,23 @@
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __setattr__(self, name, value):
+        if hasattr(self, '_dirty') and name != '_dirty' and (not hasattr(self, name) or value != getattr(self, name)):
+            self._dirty.add(name)
+        super(Model, self).__setattr__(name, value)
+
+    def _wash(self):
+        self._dirty = set()
+
     def __init__(self, *args, **kwargs):
         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
 
+        dirty = set()
+
         # 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
+        # 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
-        # args, and nstantiation for iteration is 33% faster.
+        # args, and instantiation for iteration is 33% faster.
         args_len = len(args)
         if args_len > len(self._meta.fields):
             # Daft, but matches old exception sans the err msg.
@@ -117,10 +132,12 @@
             # without changing the logic.
             for val, field in izip(args, fields_iter):
                 setattr(self, field.attname, val)
+                dirty.add(field.attname)
         else:
             # Slower, kwargs-ready version.
             for val, field in izip(args, fields_iter):
                 setattr(self, field.attname, val)
+                dirty.add(field.attname)
                 kwargs.pop(field.name, None)
                 # Maintain compatibility with existing calls.
                 if isinstance(field.rel, ManyToOneRel):
@@ -130,6 +147,8 @@
         # keywords, or default.
 
         for field in fields_iter:
+            if field.attname in kwargs or field.name in kwargs:
+                dirty.add(field.attname)
             if kwargs:
                 if isinstance(field.rel, ManyToOneRel):
                     try:
@@ -167,6 +186,9 @@
                     pass
             if kwargs:
                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
+
+        self._wash()
+        self._dirty = dirty
         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
 
     def add_to_class(cls, name, value):
@@ -202,9 +224,11 @@
     _prepare = classmethod(_prepare)
 
     def save(self, raw=False):
+        "Save model object to database, creating if doesn't exist, otherwise updating"
         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 (f.name in self._dirty or f.attname in self._dirty)]
+        self._wash()
         cursor = connection.cursor()
 
         # First, try an UPDATE. If that doesn't update anything, do an INSERT.
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 5832)
+++ django/db/models/query.py	(working copy)
@@ -203,6 +203,7 @@
                                                     index_start=0, max_depth=self._max_related_depth)
                 else:
                     obj = self.model(*row[:index_end])
+                    obj._wash()
                 for i, k in enumerate(extra_select):
                     setattr(obj, k[0], row[index_end+i])
                 yield obj
Index: tests/modeltests/update_fields/__init__.py
===================================================================
Index: tests/modeltests/update_fields/models.py
===================================================================
--- tests/modeltests/update_fields/models.py	(revision 0)
+++ tests/modeltests/update_fields/models.py	(revision 0)
@@ -0,0 +1,180 @@
+"""
+#. Save only modified fields on update
+"""
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    body = models.TextField()
+    pub_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('pub_date','headline')
+
+    def __str__(self):
+        return self.headline
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Articles.
+>>> from datetime import datetime
+>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5))
+>>> a.save()
+
+# Change headline and body on different instances, then save both to verify
+# that each only updated the modified field
+>>> a1 = Article.objects.get(pk=a.id)
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a1.headline = 'Changed article 1'
+>>> a2.body = 'Changed body 1'
+>>> a1.save()
+>>> a2.save()
+>>> a3 = Article.objects.get(pk=a.id)
+>>> a3.headline
+'Changed article 1'
+>>> a3.body
+'Changed body 1'
+"""}
+"""
+#. Save only modified fields on update
+"""
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    body = models.TextField()
+    pub_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('pub_date','headline')
+
+    def __str__(self):
+        return self.headline
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Articles.
+>>> from datetime import datetime
+>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5))
+>>> a.save()
+
+# Change headline and body on different instances, then save both to verify
+# that each only updated the modified field
+>>> a1 = Article.objects.get(pk=a.id)
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a1.headline = 'Changed article 1'
+>>> a2.body = 'Changed body 1'
+>>> a1.save()
+>>> a2.save()
+>>> a3 = Article.objects.get(pk=a.id)
+>>> a3.headline
+'Changed article 1'
+>>> a3.body
+'Changed body 1'
+"""}
+"""
+#. Save only modified fields on update
+"""
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    body = models.TextField()
+    pub_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('pub_date','headline')
+
+    def __str__(self):
+        return self.headline
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Articles.
+>>> from datetime import datetime
+>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5))
+>>> a.save()
+
+# Change headline and body on different instances, then save both to verify
+# that each only updated the modified field
+>>> a1 = Article.objects.get(pk=a.id)
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a1.headline = 'Changed article 1'
+>>> a2.body = 'Changed body 1'
+>>> a1.save()
+>>> a2.save()
+>>> a3 = Article.objects.get(pk=a.id)
+>>> a3.headline
+'Changed article 1'
+>>> a3.body
+'Changed body 1'
+"""}
+"""
+#. Save only modified fields on update
+"""
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    body = models.TextField()
+    pub_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('pub_date','headline')
+
+    def __str__(self):
+        return self.headline
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Articles.
+>>> from datetime import datetime
+>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5))
+>>> a.save()
+
+# Change headline and body on different instances, then save both to verify
+# that each only updated the modified field
+>>> a1 = Article.objects.get(pk=a.id)
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a1.headline = 'Changed article 1'
+>>> a2.body = 'Changed body 1'
+>>> a1.save()
+>>> a2.save()
+>>> a3 = Article.objects.get(pk=a.id)
+>>> a3.headline
+'Changed article 1'
+>>> a3.body
+'Changed body 1'
+"""}
+"""
+#. Save only modified fields on update
+"""
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    body = models.TextField()
+    pub_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('pub_date','headline')
+
+    def __str__(self):
+        return self.headline
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Articles.
+>>> from datetime import datetime
+>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5))
+>>> a.save()
+
+# Change headline and body on different instances, then save both to verify
+# that each only updated the modified field
+>>> a1 = Article.objects.get(pk=a.id)
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a1.headline = 'Changed article 1'
+>>> a2.body = 'Changed body 1'
+>>> a1.save()
+>>> a2.save()
+>>> a3 = Article.objects.get(pk=a.id)
+>>> a3.headline
+'Changed article 1'
+>>> a3.body
+'Changed body 1'
+"""}
