Author: Alex
Date: 2009-12-03 00:25:45 -0600 (Thu, 03 Dec 2009)
New Revision: 11786

Modified:
   django/branches/soc2009/multidb/TODO
   django/branches/soc2009/multidb/django/db/models/fields/__init__.py
   django/branches/soc2009/multidb/django/db/models/fields/files.py
   django/branches/soc2009/multidb/django/db/models/fields/related.py
   django/branches/soc2009/multidb/django/db/models/fields/subclassing.py
   django/branches/soc2009/multidb/django/db/models/query.py
   django/branches/soc2009/multidb/django/db/models/related.py
   django/branches/soc2009/multidb/django/db/models/sql/compiler.py
   django/branches/soc2009/multidb/django/db/models/sql/expressions.py
   django/branches/soc2009/multidb/django/db/models/sql/query.py
   django/branches/soc2009/multidb/django/db/models/sql/where.py
   django/branches/soc2009/multidb/docs/howto/custom-model-fields.txt
   django/branches/soc2009/multidb/docs/internals/deprecation.txt
   django/branches/soc2009/multidb/docs/releases/1.2.txt
Log:
[soc2009/multidb] Cleaned up the double processing required by validate() by 
splitting get_db_prep_* functions into db-specific and non-db-specific parts.  
Patch from Russell Keith-Magee.

Modified: django/branches/soc2009/multidb/TODO
===================================================================
--- django/branches/soc2009/multidb/TODO        2009-12-02 00:30:00 UTC (rev 
11785)
+++ django/branches/soc2009/multidb/TODO        2009-12-03 06:25:45 UTC (rev 
11786)
@@ -7,10 +7,6 @@
  * Finalize the sql.Query internals
    * Clean up the use of db.backend.query_class()
    * Verify it still works with GeoDjango
- * Cleanup of new API entry points
-    * validate() on a field
-        * name/purpose clash with Honza?
-        * any overlap with existing methods?
 
 Optional for v1.2
 ~~~~~~~~~~~~~~~~~

Modified: django/branches/soc2009/multidb/django/db/models/fields/__init__.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/fields/__init__.py 
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/fields/__init__.py 
2009-12-03 06:25:45 UTC (rev 11786)
@@ -180,21 +180,56 @@
         "Returns field's value just before saving."
         return getattr(model_instance, self.attname)
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
+        "Perform preliminary non-db specific value checks and conversions."
+        return value
+
+    def get_db_prep_value(self, value, connection, prepared=False):
         """Returns field's value prepared for interacting with the database
         backend.
 
         Used by the default implementations of ``get_db_prep_save``and
         `get_db_prep_lookup```
         """
+        if not prepared:
+            value = self.get_prep_value(value)
         return value
 
     def get_db_prep_save(self, value, connection):
         "Returns field's value prepared for saving into a database."
-        return self.get_db_prep_value(value, connection=connection)
+        return self.get_db_prep_value(value, connection=connection, 
prepared=False)
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_prep_lookup(self, lookup_type, value):
+        "Perform preliminary non-db specific lookup checks and conversions"
+        if hasattr(value, 'prepare'):
+            return value.prepare()
+        if hasattr(value, '_prepare'):
+            return value._prepare()
+
+        if lookup_type in (
+                'regex', 'iregex', 'month', 'day', 'week_day', 'search',
+                'contains', 'icontains', 'iexact', 'startswith', 'istartswith',
+                'endswith', 'iendswith', 'isnull'
+            ):
+            return value
+        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
+            return self.get_prep_value(value)
+        elif lookup_type in ('range', 'in'):
+            return [self.get_prep_value(v) for v in value]
+        elif lookup_type == 'year':
+            try:
+                return int(value)
+            except ValueError:
+                raise ValueError("The __year lookup type requires an integer 
argument")
+
+        raise TypeError("Field has invalid lookup: %s" % lookup_type)
+
+    def get_db_prep_lookup(self, lookup_type, value, connection, 
prepared=False):
         "Returns field's value prepared for database lookup."
+        if not prepared:
+            value = self.get_prep_lookup(lookup_type, value)
+        if hasattr(value, 'get_compiler'):
+            value = value.get_compiler(connection=connection)
         if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'):
             # If the value has a relabel_aliases method, it will need to
             # be invoked before the final SQL is evaluated
@@ -206,13 +241,12 @@
                 sql, params = value._as_sql(connection=connection)
             return QueryWrapper(('(%s)' % sql), params)
 
-
         if lookup_type in ('regex', 'iregex', 'month', 'day', 'week_day', 
'search'):
             return [value]
         elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
-            return [self.get_db_prep_value(value, connection=connection)]
+            return [self.get_db_prep_value(value, connection=connection, 
prepared=prepared)]
         elif lookup_type in ('range', 'in'):
-            return [self.get_db_prep_value(v, connection=connection) for v in 
value]
+            return [self.get_db_prep_value(v, connection=connection, 
prepared=prepared) for v in value]
         elif lookup_type in ('contains', 'icontains'):
             return ["%%%s%%" % connection.ops.prep_for_like_query(value)]
         elif lookup_type == 'iexact':
@@ -224,36 +258,11 @@
         elif lookup_type == 'isnull':
             return []
         elif lookup_type == 'year':
-            try:
-                value = int(value)
-            except ValueError:
-                raise ValueError("The __year lookup type requires an integer 
argument")
-
             if self.get_internal_type() == 'DateField':
                 return connection.ops.year_lookup_bounds_for_date_field(value)
             else:
                 return connection.ops.year_lookup_bounds(value)
 
-        raise TypeError("Field has invalid lookup: %s" % lookup_type)
-
-    def validate(self, lookup_type, value):
-        """
-        Validate that the data is valid, as much so as possible without knowing
-        what connection we are using.  Returns True if the value was
-        successfully validated and false if the value wasn't validated (this
-        doesn't consider whether the value was actually valid, an exception is
-        raised in those circumstances).
-        """
-        if hasattr(value, 'validate') or hasattr(value, '_validate'):
-            if hasattr(value, 'validate'):
-                value.validate()
-            else:
-                value._validate()
-            return True
-        if lookup_type == 'isnull':
-            return True
-        return False
-
     def has_default(self):
         "Returns a boolean of whether this field has a default value."
         return self.default is not NOT_PROVIDED
@@ -376,22 +385,11 @@
             raise exceptions.ValidationError(
                 _("This value must be an integer."))
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         if value is None:
             return None
         return int(value)
 
-    def validate(self, lookup_type, value):
-        if super(AutoField, self).validate(lookup_type, value):
-            return
-        if value is None or hasattr(value, 'as_sql'):
-            return
-        if lookup_type in ('range', 'in'):
-            for val in value:
-                int(val)
-        else:
-            int(value)
-
     def contribute_to_class(self, cls, name):
         assert not cls._meta.has_auto_field, "A model can't have more than one 
AutoField."
         super(AutoField, self).contribute_to_class(cls, name)
@@ -419,24 +417,16 @@
         raise exceptions.ValidationError(
             _("This value must be either True or False."))
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_prep_lookup(self, lookup_type, value):
         # Special-case handling for filters coming from a web request (e.g. the
         # admin interface). Only works for scalar values (not lists). If you're
         # passing in a list, you might as well make things the right type when
         # constructing the list.
         if value in ('1', '0'):
             value = bool(int(value))
-        return super(BooleanField, self).get_db_prep_lookup(lookup_type, value,
-            connection=connection)
+        return super(BooleanField, self).get_prep_lookup(lookup_type, value)
 
-    def validate(self, lookup_type, value):
-        if super(BooleanField, self).validate(lookup_type, value):
-            return
-        if value in ('1', '0'):
-            value = int(value)
-        bool(value)
-
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         if value is None:
             return None
         return bool(value)
@@ -539,32 +529,22 @@
             setattr(cls, 'get_previous_by_%s' % self.name,
                 curry(cls._get_next_or_previous_by_FIELD, field=self, 
is_next=False))
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_prep_lookup(self, lookup_type, value):
         # For "__month", "__day", and "__week_day" lookups, convert the value
         # to an int so the database backend always sees a consistent type.
         if lookup_type in ('month', 'day', 'week_day'):
-            return [int(value)]
-        return super(DateField, self).get_db_prep_lookup(lookup_type, value,
-            connection=connection)
+            return int(value)
+        return super(DateField, self).get_prep_lookup(lookup_type, value)
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
+        return self.to_python(value)
+
+    def get_db_prep_value(self, value, connection, prepared=False):
         # Casts dates into the format expected by the backend
-        return connection.ops.value_to_db_date(self.to_python(value))
+        if not prepared:
+            value = self.get_prep_value(value)
+        return connection.ops.value_to_db_date(value)
 
-    def validate(self, lookup_type, value):
-        if super(DateField, self).validate(lookup_type, value):
-            return
-        if value is None:
-            return
-        if lookup_type in ('month', 'day', 'year', 'week_day'):
-            int(value)
-            return
-        if lookup_type in ('in', 'range'):
-            for val in value:
-                self.to_python(val)
-            return
-        self.to_python(value)
-
     def value_to_string(self, obj):
         val = self._get_val_from_obj(obj)
         if val is None:
@@ -619,9 +599,14 @@
                     raise exceptions.ValidationError(
                         _('Enter a valid date/time in YYYY-MM-DD 
HH:MM[:ss[.uuuuuu]] format.'))
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
+        return self.to_python(value)
+
+    def get_db_prep_value(self, value, connection, prepared=False):
         # Casts dates into the format expected by the backend
-        return connection.ops.value_to_db_datetime(self.to_python(value))
+        if not prepared:
+            value = self.get_prep_value(value)
+        return connection.ops.value_to_db_datetime(value)
 
     def value_to_string(self, obj):
         val = self._get_val_from_obj(obj)
@@ -679,7 +664,7 @@
         return connection.ops.value_to_db_decimal(self.to_python(value),
                 self.max_digits, self.decimal_places)
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         return self.to_python(value)
 
     def formfield(self, **kwargs):
@@ -723,7 +708,7 @@
 class FloatField(Field):
     empty_strings_allowed = False
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         if value is None:
             return None
         return float(value)
@@ -747,7 +732,7 @@
 
 class IntegerField(Field):
     empty_strings_allowed = False
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         if value is None:
             return None
         return int(value)
@@ -800,22 +785,16 @@
         raise exceptions.ValidationError(
             _("This value must be either None, True or False."))
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_prep_lookup(self, lookup_type, value):
         # Special-case handling for filters coming from a web request (e.g. the
         # admin interface). Only works for scalar values (not lists). If you're
         # passing in a list, you might as well make things the right type when
         # constructing the list.
         if value in ('1', '0'):
             value = bool(int(value))
-        return super(NullBooleanField, self).get_db_prep_lookup(lookup_type,
-            value, connection=connection)
+        return super(NullBooleanField, self).get_prep_lookup(lookup_type, 
value)
 
-    def validate(self, lookup_type, value):
-        if value in ('1', '0'):
-            value = int(value)
-        bool(value)
-
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         if value is None:
             return None
         return bool(value)
@@ -931,9 +910,14 @@
         else:
             return super(TimeField, self).pre_save(model_instance, add)
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
+        return self.to_python(value)
+
+    def get_db_prep_value(self, value, connection, prepared=False):
         # Casts times into the format expected by the backend
-        return connection.ops.value_to_db_time(self.to_python(value))
+        if not prepared:
+            value = self.get_prep_value(value)
+        return connection.ops.value_to_db_time(value)
 
     def value_to_string(self, obj):
         val = self._get_val_from_obj(obj)

Modified: django/branches/soc2009/multidb/django/db/models/fields/files.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/fields/files.py    
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/fields/files.py    
2009-12-03 06:25:45 UTC (rev 11786)
@@ -232,13 +232,12 @@
     def get_internal_type(self):
         return "FileField"
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_prep_lookup(self, lookup_type, value):
         if hasattr(value, 'name'):
             value = value.name
-        return super(FileField, self).get_db_prep_lookup(lookup_type, value,
-            connection=connection)
+        return super(FileField, self).get_prep_lookup(lookup_type, value)
 
-    def get_db_prep_value(self, value, connection):
+    def get_prep_value(self, value):
         "Returns field's value prepared for saving into a database."
         # Need to convert File objects provided via a form to unicode for 
database insertion
         if value is None:

Modified: django/branches/soc2009/multidb/django/db/models/fields/related.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/fields/related.py  
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/fields/related.py  
2009-12-03 06:25:45 UTC (rev 11786)
@@ -120,7 +120,7 @@
         if not cls._meta.abstract:
             self.contribute_to_related_class(other, self.related)
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_db_prep_lookup(self, lookup_type, value, connection, 
prepared=False):
         # If we are doing a lookup on a Related Field, we must be
         # comparing object instances. The value should be the PK of value,
         # not value itself.
@@ -140,14 +140,16 @@
             if field:
                 if lookup_type in ('range', 'in'):
                     v = [v]
-                v = field.get_db_prep_lookup(lookup_type, v, 
connection=connection)
+                v = field.get_db_prep_lookup(lookup_type, v,
+                        connection=connection, prepared=prepared)
                 if isinstance(v, list):
                     v = v[0]
             return v
 
+        if not prepared:
+            value = self.get_prep_lookup(lookup_type, value)
         if hasattr(value, 'get_compiler'):
             value = value.get_compiler(connection=connection)
-
         if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'):
             # If the value has a relabel_aliases method, it will need to
             # be invoked before the final SQL is evaluated

Modified: django/branches/soc2009/multidb/django/db/models/fields/subclassing.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/fields/subclassing.py      
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/fields/subclassing.py      
2009-12-03 06:25:45 UTC (rev 11786)
@@ -11,25 +11,53 @@
 
 def call_with_connection(func):
     arg_names, varargs, varkwargs, defaults = getargspec(func)
-    takes_connection = 'connection' in arg_names or varkwargs
-    if not takes_connection:
-        warn("A Field class whose %s method doesn't take connection has been "
-            "defined. Please add a connection argument" % func.__name__,
+    updated = ('connection' in arg_names or varkwargs)
+    if not updated:
+        warn("A Field class whose %s method hasn't been updated to take a "
+            "`connection` argument." % func.__name__,
             PendingDeprecationWarning, stacklevel=2)
+
     def inner(*args, **kwargs):
         if 'connection' not in kwargs:
             from django.db import connection
             kwargs['connection'] = connection
-            warn("%s has been called without providing a connection argument. "
-                "Please provide one" % func.__name__, 
PendingDeprecationWarning,
+            warn("%s has been called without providing a connection argument. 
" %
+                func.__name__, PendingDeprecationWarning,
                 stacklevel=1)
-        if takes_connection:
+        if updated:
             return func(*args, **kwargs)
         if 'connection' in kwargs:
             del kwargs['connection']
         return func(*args, **kwargs)
     return inner
 
+def call_with_connection_and_prepared(func):
+    arg_names, varargs, varkwargs, defaults = getargspec(func)
+    updated = (
+        ('connection' in arg_names or varkwargs) and
+        ('prepared' in arg_names or varkwargs)
+    )
+    if not updated:
+        warn("A Field class whose %s method hasn't been updated to take "
+            "`connection` and `prepared` arguments." % func.__name__,
+            PendingDeprecationWarning, stacklevel=2)
+
+    def inner(*args, **kwargs):
+        if 'connection' not in kwargs:
+            from django.db import connection
+            kwargs['connection'] = connection
+            warn("%s has been called without providing a connection argument. 
" %
+                func.__name__, PendingDeprecationWarning,
+                stacklevel=1)
+        if updated:
+            return func(*args, **kwargs)
+        if 'connection' in kwargs:
+            del kwargs['connection']
+        if 'prepared' in kwargs:
+            del kwargs['prepared']
+        return func(*args, **kwargs)
+    return inner
+
 class LegacyConnection(type):
     """
     A metaclass to normalize arguments give to the get_db_prep_* and db_type
@@ -37,9 +65,10 @@
     """
     def __new__(cls, names, bases, attrs):
         new_cls = super(LegacyConnection, cls).__new__(cls, names, bases, 
attrs)
-        for attr in ('db_type', 'get_db_prep_save', 'get_db_prep_lookup',
-            'get_db_prep_value'):
+        for attr in ('db_type', 'get_db_prep_save'):
             setattr(new_cls, attr, call_with_connection(getattr(new_cls, 
attr)))
+        for attr in ('get_db_prep_lookup', 'get_db_prep_value'):
+            setattr(new_cls, attr, 
call_with_connection_and_prepared(getattr(new_cls, attr)))
         return new_cls
 
 class SubfieldBase(LegacyConnection):

Modified: django/branches/soc2009/multidb/django/db/models/query.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/query.py   2009-12-02 
00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/query.py   2009-12-03 
06:25:45 UTC (rev 11786)
@@ -739,6 +739,9 @@
             self.query.add_fields(field_names, False)
             self.query.set_group_by()
 
+    def _prepare(self):
+        return self
+
     def _as_sql(self, connection):
         """
         Returns the internal query's SQL and parameters (as a tuple).
@@ -748,13 +751,6 @@
             return 
obj.query.get_compiler(connection=connection).as_nested_sql()
         raise ValueError("Can't do subqueries with queries on different DBs.")
 
-    def _validate(self):
-        """
-        A normal QuerySet is always valid when used as the RHS of a filter,
-        since it automatically gets filtered down to 1 field.
-        """
-        pass
-
     # When used as part of a nested query, a queryset will never be an "always
     # empty" result.
     value_annotation = True
@@ -877,7 +873,7 @@
             return 
obj.query.get_compiler(connection=connection).as_nested_sql()
         raise ValueError("Can't do subqueries with queries on different DBs.")
 
-    def _validate(self):
+    def _prepare(self):
         """
         Validates that we aren't trying to do a query like
         value__in=qs.values('value1', 'value2'), which isn't valid.
@@ -886,8 +882,8 @@
                 (not self._fields and len(self.model._meta.fields) > 1)):
             raise TypeError('Cannot use a multi-field %s as a filter value.'
                     % self.__class__.__name__)
+        return self
 
-
 class ValuesListQuerySet(ValuesQuerySet):
     def iterator(self):
         if self.flat and len(self._fields) == 1:

Modified: django/branches/soc2009/multidb/django/db/models/related.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/related.py 2009-12-02 
00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/related.py 2009-12-03 
06:25:45 UTC (rev 11786)
@@ -18,9 +18,10 @@
         self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name)
         self.var_name = self.opts.object_name.lower()
 
-    def get_db_prep_lookup(self, lookup_type, value, connection):
+    def get_db_prep_lookup(self, lookup_type, value, connection, 
prepared=False):
         # Defer to the actual field definition for db prep
-        return self.field.get_db_prep_lookup(lookup_type, value)
+        return self.field.get_db_prep_lookup(lookup_type, value,
+                        connection=connection, prepared=prepared)
 
     def editable_fields(self):
         "Get the fields in this class that should be edited inline."

Modified: django/branches/soc2009/multidb/django/db/models/sql/compiler.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/sql/compiler.py    
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/sql/compiler.py    
2009-12-03 06:25:45 UTC (rev 11786)
@@ -837,7 +837,7 @@
             self.query.related_ids = idents
         else:
             # The fast path. Filters and updates in one query.
-            self.query.add_filter(('pk__in', query.get_compiler(self.using)))
+            self.query.add_filter(('pk__in', query))
         for alias in self.query.tables[1:]:
             self.query.alias_refcount[alias] = 0
 

Modified: django/branches/soc2009/multidb/django/db/models/sql/expressions.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/sql/expressions.py 
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/sql/expressions.py 
2009-12-03 06:25:45 UTC (rev 11786)
@@ -11,6 +11,9 @@
         self.contains_aggregate = False
         self.expression.prepare(self, query, allow_joins)
 
+    def prepare(self):
+        return self
+
     def as_sql(self, qn, connection):
         return self.expression.evaluate(self, qn, connection)
 

Modified: django/branches/soc2009/multidb/django/db/models/sql/query.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/sql/query.py       
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/sql/query.py       
2009-12-03 06:25:45 UTC (rev 11786)
@@ -141,6 +141,9 @@
 
         self.__dict__.update(obj_dict)
 
+    def prepare(self):
+        return self
+
     def get_compiler(self, using=None, connection=None):
         if using is None and connection is None:
             raise ValueError("Need either using or connection")

Modified: django/branches/soc2009/multidb/django/db/models/sql/where.py
===================================================================
--- django/branches/soc2009/multidb/django/db/models/sql/where.py       
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/django/db/models/sql/where.py       
2009-12-03 06:25:45 UTC (rev 11786)
@@ -62,8 +62,8 @@
         else:
             annotation = bool(value)
 
-        if hasattr(obj, "process"):
-            obj.validate(lookup_type, value)
+        if hasattr(obj, "prepare"):
+            value = obj.prepare(lookup_type, value)
             super(WhereNode, self).add((obj, lookup_type, annotation, value),
                 connector)
             return
@@ -143,7 +143,7 @@
                 raise EmptyResultSet
         else:
             params = Field().get_db_prep_lookup(lookup_type, params_or_value,
-                connection=connection)
+                connection=connection, prepared=True)
         if isinstance(lvalue, tuple):
             # A direct database column lookup.
             field_sql = self.sql_for_columns(lvalue, qn, connection)
@@ -262,6 +262,11 @@
     def __init__(self, alias, col, field):
         self.alias, self.col, self.field = alias, col, field
 
+    def prepare(self, lookup_type, value):
+        if self.field:
+            return self.field.get_prep_lookup(lookup_type, value)
+        return value
+
     def process(self, lookup_type, value, connection):
         """
         Returns a tuple of data suitable for inclusion in a WhereNode
@@ -272,14 +277,14 @@
         try:
             if self.field:
                 params = self.field.get_db_prep_lookup(lookup_type, value,
-                    connection=connection)
+                    connection=connection, prepared=True)
                 db_type = self.field.db_type(connection=connection)
             else:
                 # This branch is used at times when we add a comparison to NULL
                 # (we don't really want to waste time looking up the associated
                 # field object at the calling location).
                 params = Field().get_db_prep_lookup(lookup_type, value,
-                    connection=connection)
+                    connection=connection, prepared=True)
                 db_type = None
         except ObjectDoesNotExist:
             raise EmptyShortCircuit
@@ -289,7 +294,3 @@
     def relabel_aliases(self, change_map):
         if self.alias in change_map:
             self.alias = change_map[self.alias]
-
-    def validate(self, lookup_type, value):
-        if hasattr(self.field, 'validate'):
-            self.field.validate(lookup_type, value)

Modified: django/branches/soc2009/multidb/docs/howto/custom-model-fields.txt
===================================================================
--- django/branches/soc2009/multidb/docs/howto/custom-model-fields.txt  
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/docs/howto/custom-model-fields.txt  
2009-12-03 06:25:45 UTC (rev 11786)
@@ -396,36 +396,58 @@
 called when it is created, you should be using `The SubfieldBase metaclass`_
 mentioned earlier. Otherwise :meth:`to_python` won't be called automatically.
 
-Converting Python objects to database values
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Converting Python objects to query values
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. method:: get_db_prep_value(self, value, connection)
+.. method:: get_prep_value(self, value)
 
-This is the reverse of :meth:`to_python` when working with the database 
backends
-(as opposed to serialization). The ``value`` parameter is the current value of
-the model's attribute (a field has no reference to its containing model, so it
-cannot retrieve the value itself), and the method should return data in a 
format
-that can be used as a parameter in a query for the database backend.  The
-specific connection that will be used for the query is passed as the
-``connection`` parameter, this allows you to generate the value in a backend
-specific mannner if necessary.
+This is the reverse of :meth:`to_python` when working with the
+database backends (as opposed to serialization). The ``value``
+parameter is the current value of the model's attribute (a field has
+no reference to its containing model, so it cannot retrieve the value
+itself), and the method should return data in a format that has been
+prepared for use as a parameter in a query.
 
+This conversion should *not* include any database-specific
+conversions. If database-specific conversions are required, they
+should be made in the call to :meth:`get_db_prep_value`.
+
 For example::
 
     class HandField(models.Field):
         # ...
 
-        def get_db_prep_value(self, value, connection):
+        def get_prep_value(self, value):
             return ''.join([''.join(l) for l in (value.north,
                     value.east, value.south, value.west)])
 
+Converting query values to database values
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: get_db_prep_value(self, value, connection, prepared=False)
+
+Some data types (for example, dates) need to be in a specific format
+before they can be used by a database backend.
+:meth:`get_db_prep_value` is the method where those conversions should
+be made. The specific connection that will be used for the query is
+passed as the ``connection`` parameter. This allows you to use
+backend-specific conversion logic if it is required.
+
+The ``prepared`` argument describes whether or not the value has
+already been passed through :meth:`get_prep_value` conversions. When
+``prepared`` is False, the default implementation of
+:meth:`get_db_prep_value` will call :meth:`get_prep_value` to do
+initial data conversions before performing any database-specific
+processing.
+
 .. method:: get_db_prep_save(self, value, connection)
 
-Same as the above, but called when the Field value must be *saved* to the
-database. As the default implementation just calls ``get_db_prep_value``, you
-shouldn't need to implement this method unless your custom field needs a
-special conversion when being saved that is not the same as the conversion used
-for normal query parameters (which is implemented by ``get_db_prep_value``).
+Same as the above, but called when the Field value must be *saved* to
+the database. As the default implementation just calls
+``get_db_prep_value``, you shouldn't need to implement this method
+unless your custom field needs a special conversion when being saved
+that is not the same as the conversion used for normal query
+parameters (which is implemented by ``get_db_prep_value``).
 
 Preprocessing values before saving
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -453,8 +475,14 @@
 Preparing values for use in database lookups
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. method:: get_db_prep_lookup(self, lookup_type, value, connection)
+As with value conversions, preparing a value for database lookups is a
+two phase process.
 
+.. method:: get_prep_lookup(self, lookup_type, value)
+
+:meth:`get_prep_lookup` performs the first phase of lookup preparation,
+performing generic data validity checks
+
 Prepares the ``value`` for passing to the database when used in a lookup (a
 ``WHERE`` constraint in SQL). The ``lookup_type`` will be one of the valid
 Django filter lookups: ``exact``, ``iexact``, ``contains``, ``icontains``,
@@ -470,34 +498,42 @@
 and pass the rest to the :meth:`get_db_prep_lookup` method of the parent class.
 
 If you needed to implement ``get_db_prep_save()``, you will usually need to
-implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be
+implement ``get_prep_lookup()``. If you don't, ``get_prep_value`` will be
 called by the default implementation, to manage ``exact``, ``gt``, ``gte``,
 ``lt``, ``lte``, ``in`` and ``range`` lookups.
 
 You may also want to implement this method to limit the lookup types that could
 be used with your custom field type.
 
-Note that, for ``range`` and ``in`` lookups, ``get_db_prep_lookup`` will 
receive
+Note that, for ``range`` and ``in`` lookups, ``get_prep_lookup`` will receive
 a list of objects (presumably of the right type) and will need to convert them
 to a list of things of the right type for passing to the database. Most of the
-time, you can reuse ``get_db_prep_value()``, or at least factor out some common
+time, you can reuse ``get_prep_value()``, or at least factor out some common
 pieces.
 
-For example, the following code implements ``get_db_prep_lookup`` to limit the
+For example, the following code implements ``get_prep_lookup`` to limit the
 accepted lookup types to ``exact`` and ``in``::
 
     class HandField(models.Field):
         # ...
 
-        def get_db_prep_lookup(self, lookup_type, value):
+        def get_prep_lookup(self, lookup_type, value):
             # We only handle 'exact' and 'in'. All others are errors.
             if lookup_type == 'exact':
-                return [self.get_db_prep_value(value)]
+                return [self.get_prep_value(value)]
             elif lookup_type == 'in':
-                return [self.get_db_prep_value(v) for v in value]
+                return [self.get_prep_value(v) for v in value]
             else:
                 raise TypeError('Lookup type %r not supported.' % lookup_type)
 
+.. method:: get_db_prep_lookup(self, lookup_type, value, connection, 
prepared=False)
+
+Performs any database-specific data conversions required by a lookup.
+As with :meth:`get_db_prep_value`, the specific connection that will
+be used for the query is passed as the ``connection`` parameter.
+The ``prepared`` argument describes whether the value has already been
+prepared with :meth:`get_prep_lookup`.
+
 Specifying the form field for a model field
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

Modified: django/branches/soc2009/multidb/docs/internals/deprecation.txt
===================================================================
--- django/branches/soc2009/multidb/docs/internals/deprecation.txt      
2009-12-02 00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/docs/internals/deprecation.txt      
2009-12-03 06:25:45 UTC (rev 11786)
@@ -35,6 +35,11 @@
           (i.e., ``sqlite3`` instead of ``django.db.backends.sqlite3``) will be
           removed.
 
+        * The ``get_db_prep_save``, ``get_db_prep_value`` and
+          ``get_db_prep_lookup`` methods on Field were modified in 1.2 to 
support
+          multiple databases. In 1.4, the support functions that allow methods
+          with the old prototype to continue working will be removed.
+
     * 2.0
         * ``django.views.defaults.shortcut()``. This function has been moved
           to ``django.contrib.contenttypes.views.shortcut()`` as part of the

Modified: django/branches/soc2009/multidb/docs/releases/1.2.txt
===================================================================
--- django/branches/soc2009/multidb/docs/releases/1.2.txt       2009-12-02 
00:30:00 UTC (rev 11785)
+++ django/branches/soc2009/multidb/docs/releases/1.2.txt       2009-12-03 
06:25:45 UTC (rev 11786)
@@ -141,6 +141,74 @@
 iterating over __dict__ to obtain a list of fields, you must now
 filter out ``_state`` attribute of out ``__dict__``.
 
+``get_db_prep_*()`` methods on Field
+------------------------------------
+
+Prior to v1.2, a custom field had the option of defining several
+functions to support conversion of Python values into
+database-compatible values. A custom field might look something like::
+
+    class CustomModelField(models.Field):
+        # ...
+
+        def get_db_prep_save(self, value):
+            # ...
+
+        def get_db_prep_value(self, value):
+            # ...
+
+        def get_db_prep_lookup(self, lookup_type, value):
+            # ...
+
+In 1.2, these three methods have undergone a change in prototype, and
+two extra methods have been introduced::
+
+    class CustomModelField(models.Field):
+        # ...
+
+        def get_prep_value(self, value):
+            # ...
+
+        def get_prep_lookup(self, lookup_type, value):
+            # ...
+
+        def get_db_prep_save(self, value, connection):
+            # ...
+
+        def get_db_prep_value(self, value, connection, prepared=False):
+            # ...
+
+        def get_prep_lookup(self, lookup_type, value, connection, 
prepared=False):
+            # ...
+
+These changes are required to support multiple databases -
+``get_db_prep_*`` can no longer make any assumptions regarding the
+database for which it is preparing. The ``connection`` argument now
+provides the preparation methods with the specific connection for
+which the value is being prepared.
+
+The two new methods exist to differentiate general data preparation
+requirements, and requirements that are database-specific. The
+``prepared`` argument is used to indicate to the database preparation
+methods whether generic value preparation has been performed. If
+an unprepared (i.e., ``prepared=False``) value is provided to the
+``get_db_prep_*()`` calls, they should invoke the corresponding
+``get_prep_*()`` calls to perform generic data preparation.
+
+Conversion functions has been provided which will transparently
+convert functions adhering to the old prototype into functions
+compatible with the new prototype. However, this conversion function
+will be removed in Django 1.4, so you should upgrade your Field
+definitions to use the new prototype.
+
+If your ``get_db_prep_*()`` methods made no use of the database
+connection, you should be able to upgrade by renaming
+``get_db_prep_value()`` to ``get_prep_value()`` and
+``get_db_prep_lookup()`` to ``get_prep_lookup()`. If you require
+database specific conversions, then you will need to provide an
+implementation ``get_db_prep_*`` that uses the ``connection``
+argument to resolve database-specific values.
+
 .. _deprecated-features-1.2:
 
 Features deprecated in 1.2

--

You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.


Reply via email to