Index: django/db/models/sql/where.py
===================================================================
--- django/db/models/sql/where.py	(revision 7631)
+++ django/db/models/sql/where.py	(working copy)
@@ -4,9 +4,11 @@
 import datetime
 
 from django.utils import tree
+from django.utils.functional import curry
 from django.db import connection
 from django.db.models.fields import Field
 from django.db.models.query_utils import QueryWrapper
+from django.db.models.sql.expressions import Expression, Literal
 from datastructures import EmptyResultSet, FullResultSet
 
 # Connection types
@@ -26,7 +28,7 @@
     """
     default = AND
 
-    def as_sql(self, node=None, qn=None):
+    def as_sql(self, opts, node=None, qn=None):
         """
         Returns the SQL version of the where clause and the value to be
         substituted in. Returns None, None if this node is empty.
@@ -47,10 +49,10 @@
         for child in node.children:
             try:
                 if hasattr(child, 'as_sql'):
-                    sql, params = child.as_sql(qn=qn)
+                    sql, params = child.as_sql(opts, qn=qn)
                     format = '(%s)'
                 elif isinstance(child, tree.Node):
-                    sql, params = self.as_sql(child, qn)
+                    sql, params = self.as_sql(opts, child, qn)
                     if child.negated:
                         format = 'NOT (%s)'
                     elif len(child.children) == 1:
@@ -58,7 +60,7 @@
                     else:
                         format = '(%s)'
                 else:
-                    sql, params = self.make_atom(child, qn)
+                    sql, params = self.make_atom(opts, child, qn)
                     format = '%s'
             except EmptyResultSet:
                 if node.connector == AND and not node.negated:
@@ -86,7 +88,7 @@
         conn = ' %s ' % node.connector
         return conn.join(result), result_params
 
-    def make_atom(self, child, qn):
+    def make_atom(self, opts, child, qn):
         """
         Turn a tuple (table_alias, field_name, field_class, lookup_type, value)
         into valid SQL.
@@ -99,36 +101,39 @@
             lhs = '%s.%s' % (qn(table_alias), qn(name))
         else:
             lhs = qn(name)
+        if not field:
+            field = Field()
         db_type = field and field.db_type() or None
         field_sql = connection.ops.field_cast_sql(db_type) % lhs
+        prep_func = curry(field.get_db_prep_lookup, lookup_type)
 
-        if isinstance(value, datetime.datetime):
-            cast_sql = connection.ops.datetime_cast_sql()
-        else:
-            cast_sql = '%s'
+        if lookup_type in connection.operators:
+            if isinstance(value, Expression):
+                sql, params = value.as_sql(opts, field, prep_func, qn)
+            else:
+                sql, params = Literal(value).as_sql(opts, field, prep_func, qn)
 
-        if field:
-            params = field.get_db_prep_lookup(lookup_type, value)
-        else:
-            params = Field().get_db_prep_lookup(lookup_type, value)
+            format = '%s %s' % (
+                connection.ops.lookup_cast(lookup_type),
+                connection.operators[lookup_type])
+            return format % (field_sql, sql), params
+
+        if isinstance(value, Expression):
+            raise TypeError('Invalid lookup_type for use with %s object: %r' % (
+                value.__class__.__name__, lookup_type))
+
+        params = prep_func(value)
         if isinstance(params, QueryWrapper):
             extra, params = params.data
         else:
             extra = ''
 
-        if lookup_type in connection.operators:
-            format = "%s %%s %s" % (connection.ops.lookup_cast(lookup_type),
-                    extra)
-            return (format % (field_sql,
-                    connection.operators[lookup_type] % cast_sql), params)
-
         if lookup_type == 'in':
             if not value:
                 raise EmptyResultSet
             if extra:
                 return ('%s IN %s' % (field_sql, extra), params)
-            return ('%s IN (%s)' % (field_sql, ', '.join(['%s'] * len(value))),
-                    params)
+            return ('%s IN (%s)' % (field_sql, ', '.join(['%s'] * len(value))), params)
         elif lookup_type in ('range', 'year'):
             return ('%s BETWEEN %%s and %%s' % field_sql, params)
         elif lookup_type in ('month', 'day'):
@@ -164,7 +169,7 @@
     """
     A node that matches everything.
     """
-    def as_sql(self, qn=None):
+    def as_sql(self, opts, qn=None):
         raise FullResultSet
 
     def relabel_aliases(self, change_map, node=None):
Index: django/db/models/sql/query.py
===================================================================
--- django/db/models/sql/query.py	(revision 7631)
+++ django/db/models/sql/query.py	(working copy)
@@ -250,7 +250,7 @@
         # get_from_clause() for details.
         from_, f_params = self.get_from_clause()
 
-        where, w_params = self.where.as_sql(qn=self.quote_name_unless_alias)
+        where, w_params = self.where.as_sql(self.get_meta(), qn=self.quote_name_unless_alias)
         params = list(self.extra_select_params)
 
         result = ['SELECT']
Index: django/db/models/sql/subqueries.py
===================================================================
--- django/db/models/sql/subqueries.py	(revision 7631)
+++ django/db/models/sql/subqueries.py	(working copy)
@@ -8,6 +8,7 @@
 from django.db.models.sql.datastructures import RawValue, Date
 from django.db.models.sql.query import Query
 from django.db.models.sql.where import AND
+from django.db.models.sql.expressions import Expression, Literal
 
 __all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
         'CountQuery']
@@ -25,7 +26,7 @@
         assert len(self.tables) == 1, \
                 "Can only delete from one table at a time."
         result = ['DELETE FROM %s' % self.quote_name_unless_alias(self.tables[0])]
-        where, params = self.where.as_sql()
+        where, params = self.where.as_sql(self.get_meta())
         result.append('WHERE %s' % where)
         return ' '.join(result), tuple(params)
 
@@ -126,14 +127,11 @@
         result = ['UPDATE %s' % qn(table)]
         result.append('SET')
         values, update_params = [], []
-        for name, val, placeholder in self.values:
-            if val is not None:
-                values.append('%s = %s' % (qn(name), placeholder))
-                update_params.append(val)
-            else:
-                values.append('%s = NULL' % qn(name))
+        for name, sql, params in self.values:
+            values.append('%s = %s' % (qn(name), sql))
+            update_params.extend(params)
         result.append(', '.join(values))
-        where, params = self.where.as_sql()
+        where, params = self.where.as_sql(self.get_meta())
         if where:
             result.append('WHERE %s' % where)
         return ' '.join(result), tuple(update_params + params)
@@ -207,7 +205,7 @@
             self.where.add((None, f.column, f, 'in',
                     pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
                     AND)
-            self.values = [(related_field.column, None, '%s')]
+            self.values = [(related_field.column, 'NULL', ())]
             self.execute_sql(None)
 
     def add_update_values(self, values):
@@ -232,31 +230,32 @@
         """
         from django.db.models.base import Model
         for field, model, val in values_seq:
-            # FIXME: Some sort of db_prep_* is probably more appropriate here.
-            if field.rel and isinstance(val, Model):
-                val = val.pk
-
-            # Getting the placeholder for the field.
-            if hasattr(field, 'get_placeholder'):
-                placeholder = field.get_placeholder(val)
+            if isinstance(val, Expression):
+                expr = val
             else:
-                placeholder = '%s'
+                expr = Literal(val)
 
+            sql, params = expr.as_sql(
+                self.get_meta(),
+                field,
+                lambda x: field.get_db_prep_lookup('exact', field.get_db_prep_save(x)),
+                self.connection.ops.quote_name)
+
             if model:
-                self.add_related_update(model, field.column, val, placeholder)
+                self.add_related_update(model, field.column, sql, params)
             else:
-                self.values.append((field.column, val, placeholder))
+                self.values.append((field.column, sql, params))
 
-    def add_related_update(self, model, column, value, placeholder):
+    def add_related_update(self, model, column, sql, params):
         """
         Adds (name, value) to an update query for an ancestor model.
 
         Updates are coalesced so that we only run one update query per ancestor.
         """
         try:
-            self.related_updates[model].append((column, value, placeholder))
+            self.related_updates[model].append((column, sql, params))
         except KeyError:
-            self.related_updates[model] = [(column, value, placeholder)]
+            self.related_updates[model] = [(column, sql, params)]
 
     def get_related_updates(self):
         """
@@ -312,22 +311,15 @@
         parameters. This provides a way to insert NULL and DEFAULT keywords
         into the query, for example.
         """
-        placeholders, values = [], []
+        values = []
         for field, val in insert_values:
-            if hasattr(field, 'get_placeholder'):
-                # Some fields (e.g. geo fields) need special munging before
-                # they can be inserted.
-                placeholders.append(field.get_placeholder(val))
-            else:
-                placeholders.append('%s')
-
             self.columns.append(field.column)
             values.append(val)
         if raw_values:
             self.values.extend(values)
         else:
             self.params += tuple(values)
-            self.values.extend(placeholders)
+            self.values.extend(['%s'] * len(values))
 
 class DateQuery(Query):
     """
Index: django/db/models/__init__.py
===================================================================
--- django/db/models/__init__.py	(revision 7631)
+++ django/db/models/__init__.py	(working copy)
@@ -4,6 +4,7 @@
 from django.db import connection
 from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
 from django.db.models.query import Q
+from django.db.models.sql.expressions import F
 from django.db.models.manager import Manager
 from django.db.models.base import Model, AdminOptions
 from django.db.models.fields import *
Index: tests/modeltests/update/models.py
===================================================================
--- tests/modeltests/update/models.py	(revision 7631)
+++ tests/modeltests/update/models.py	(working copy)
@@ -4,64 +4,77 @@
 """
 
 from django.db import models
+from django.conf import settings
 
-class DataPoint(models.Model):
+class Product(models.Model):
     name = models.CharField(max_length=20)
-    value = models.CharField(max_length=20)
-    another_value = models.CharField(max_length=20, blank=True)
+    description = models.CharField(max_length=20)
+    expires = models.DateTimeField(null=True)
 
     def __unicode__(self):
         return unicode(self.name)
 
-class RelatedPoint(models.Model):
+class RelatedProduct(models.Model):
     name = models.CharField(max_length=20)
-    data = models.ForeignKey(DataPoint)
+    data = models.ForeignKey(Product)
 
     def __unicode__(self):
         return unicode(self.name)
 
 
 __test__ = {'API_TESTS': """
->>> DataPoint(name="d0", value="apple").save()
->>> DataPoint(name="d2", value="banana").save()
->>> d3 = DataPoint(name="d3", value="banana")
->>> d3.save()
->>> RelatedPoint(name="r1", data=d3).save()
+>>> from datetime import datetime
 
+>>> Product(name="p0", description="apple").save()
+>>> Product(name="p2", description="banana").save()
+>>> p3 = Product(name="p3", description="banana")
+>>> p3.save()
+>>> RelatedProduct(name="r1", data=p3).save()
+
 Objects are updated by first filtering the candidates into a queryset and then
 calling the update() method. It executes immediately and returns nothing.
 
->>> DataPoint.objects.filter(value="apple").update(name="d1")
->>> DataPoint.objects.filter(value="apple")
-[<DataPoint: d1>]
+>>> Product.objects.filter(description="apple").update(name="p1")
+>>> Product.objects.filter(description="apple")
+[<Product: p1>]
 
 We can update multiple objects at once.
 
->>> DataPoint.objects.filter(value="banana").update(value="pineapple")
->>> DataPoint.objects.get(name="d2").value
+>>> Product.objects.filter(description="banana").update(description="pineapple")
+>>> Product.objects.get(name="p2").description
 u'pineapple'
 
 Foreign key fields can also be updated, although you can only update the object
 referred to, not anything inside the related object.
 
->>> d = DataPoint.objects.get(name="d1")
->>> RelatedPoint.objects.filter(name="r1").update(data=d)
->>> RelatedPoint.objects.filter(data__name="d1")
-[<RelatedPoint: r1>]
+>>> p = Product.objects.get(name="p1")
+>>> RelatedProduct.objects.filter(name="r1").update(data=p)
+>>> RelatedProduct.objects.filter(data__name="p1")
+[<RelatedProduct: r1>]
 
-Multiple fields can be updated at once
+Multiple fields can be updated at once. If DATABASE_ENGINE is mysql microseconds
+must be truncated.
 
->>> DataPoint.objects.filter(value="pineapple").update(value="fruit", another_value="peaches")
->>> d = DataPoint.objects.get(name="d2")
->>> d.value, d.another_value
-(u'fruit', u'peaches')
+>>> Product.objects.filter(description="pineapple").update(
+...     description="fruit",
+...     expires=datetime(2010, 1, 1, 12, 0, 0, 123456))
+>>> p = Product.objects.get(name="p2")
+>>> p.description, p.expires
+"""}
 
+if settings.DATABASE_ENGINE == 'mysql':
+    __test__['API_TESTS'] += "(u'fruit', datetime.datetime(2010, 1, 1, 12, 0))"
+else:
+    __test__['API_TESTS'] += "(u'fruit', datetime.datetime(2010, 1, 1, 12, 0, 0, 123456))"
+
+__test__['API_TESTS'] += """
+
 In the rare case you want to update every instance of a model, update() is also
-a manager method.
+a manager method and update with None works as well.
 
->>> DataPoint.objects.update(value='thing')
->>> DataPoint.objects.values('value').distinct()
-[{'value': u'thing'}]
+>>> Product.objects.update(expires=None)
+>>> Product.objects.values('expires').distinct()
+[{'expires': None}]
 
 We do not support update on already sliced query sets.
 
@@ -71,4 +84,3 @@
 AssertionError: Cannot update a query once a slice has been taken.
 
 """
-}
