1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

[soc2009/multidb] Added connection parameter to the get_db_prep_* family of functions. This allows us to generate the lookup and save values for Fields in a backend specific manner.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11264 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-07-17 15:57:43 +00:00
parent 6060ee7c77
commit 8395544774
10 changed files with 74 additions and 50 deletions

View File

@ -18,6 +18,7 @@ from django.db.models.options import Options
from django.db import connections, transaction, DatabaseError, DEFAULT_DB_ALIAS
from django.db.models import signals
from django.db.models.loading import register_models, get_model
from django.db.utils import call_with_connection
from django.utils.functional import curry
from django.utils.encoding import smart_str, force_unicode, smart_unicode
from django.conf import settings
@ -483,9 +484,11 @@ class Model(object):
if not pk_set:
if force_update:
raise ValueError("Cannot force an update in save() with no primary key.")
values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)]
values = [(f, call_with_connection(f.get_db_prep_save, raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
for f in meta.local_fields if not isinstance(f, AutoField)]
else:
values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields]
values = [(f, call_with_connection(f.get_db_prep_save, raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
for f in meta.local_fields]
if meta.order_with_respect_to:
field = meta.order_with_respect_to

View File

@ -11,6 +11,7 @@ except ImportError:
from django.db import connection
from django.db.models import signals
from django.db.models.query_utils import QueryWrapper
from django.db.utils import call_with_connection
from django.dispatch import dispatcher
from django.conf import settings
from django import forms
@ -178,7 +179,7 @@ class Field(object):
"Returns field's value just before saving."
return getattr(model_instance, self.attname)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
"""Returns field's value prepared for interacting with the database
backend.
@ -187,11 +188,12 @@ class Field(object):
"""
return value
def get_db_prep_save(self, 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)
return call_with_connection(self.get_db_prep_value, value,
connection=connection)
def get_db_prep_lookup(self, lookup_type, value):
def get_db_prep_lookup(self, lookup_type, value, connection):
"Returns field's value prepared for database lookup."
if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'):
# If the value has a relabel_aliases method, it will need to
@ -208,9 +210,9 @@ class Field(object):
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)]
return [call_with_connection(self.get_db_prep_value, value, connection=connection)]
elif lookup_type in ('range', 'in'):
return [self.get_db_prep_value(v) for v in value]
return [call_with_connection(self.get_db_prep_value, v, connection=connection) for v in value]
elif lookup_type in ('contains', 'icontains'):
return ["%%%s%%" % connection.ops.prep_for_like_query(value)]
elif lookup_type == 'iexact':
@ -374,7 +376,7 @@ class AutoField(Field):
raise exceptions.ValidationError(
_("This value must be an integer."))
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
if value is None:
return None
return int(value)
@ -417,14 +419,15 @@ class BooleanField(Field):
raise exceptions.ValidationError(
_("This value must be either True or False."))
def get_db_prep_lookup(self, lookup_type, value):
def get_db_prep_lookup(self, lookup_type, value, connection):
# 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)
return call_with_connection(super(BooleanField, self).get_db_prep_lookup,
lookup_type, value, connection=connection)
def validate(self, lookup_type, value):
if super(BooleanField, self).validate(lookup_type, value):
@ -433,7 +436,7 @@ class BooleanField(Field):
value = int(value)
bool(value)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
if value is None:
return None
return bool(value)
@ -536,14 +539,15 @@ class DateField(Field):
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):
def get_db_prep_lookup(self, lookup_type, value, connection):
# 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)
return call_with_connection(super(DateField, self).get_db_prep_lookup,
lookup_type, value, connection=connection)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
# Casts dates into the format expected by the backend
return connection.ops.value_to_db_date(self.to_python(value))
@ -615,7 +619,7 @@ class DateTimeField(DateField):
raise exceptions.ValidationError(
_('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.'))
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
# Casts dates into the format expected by the backend
return connection.ops.value_to_db_datetime(self.to_python(value))
@ -671,11 +675,11 @@ class DecimalField(Field):
from django.db.backends import util
return util.format_number(value, self.max_digits, self.decimal_places)
def get_db_prep_save(self, value):
def get_db_prep_save(self, value, connection):
return connection.ops.value_to_db_decimal(self.to_python(value),
self.max_digits, self.decimal_places)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
return self.to_python(value)
def formfield(self, **kwargs):
@ -719,7 +723,7 @@ class FilePathField(Field):
class FloatField(Field):
empty_strings_allowed = False
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
if value is None:
return None
return float(value)
@ -743,7 +747,7 @@ class FloatField(Field):
class IntegerField(Field):
empty_strings_allowed = False
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
if value is None:
return None
return int(value)
@ -796,21 +800,22 @@ class NullBooleanField(Field):
raise exceptions.ValidationError(
_("This value must be either None, True or False."))
def get_db_prep_lookup(self, lookup_type, value):
def get_db_prep_lookup(self, lookup_type, value, connection):
# 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)
return call_with_connection(super(NullBooleanField, self).get_db_prep_lookup,
lookup_type, value, connection=connection)
def validate(self, lookup_type, value):
if value in ('1', '0'):
value = int(value)
bool(value)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
if value is None:
return None
return bool(value)
@ -926,7 +931,7 @@ class TimeField(Field):
else:
return super(TimeField, self).pre_save(model_instance, add)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
# Casts times into the format expected by the backend
return connection.ops.value_to_db_time(self.to_python(value))

View File

@ -10,6 +10,7 @@ from django.core.files.images import ImageFile, get_image_dimensions
from django.core.files.uploadedfile import UploadedFile
from django.utils.functional import curry
from django.db.models import signals
from django.db.utils import call_with_connection
from django.utils.encoding import force_unicode, smart_str
from django.utils.translation import ugettext_lazy, ugettext as _
from django import forms
@ -232,12 +233,13 @@ class FileField(Field):
def get_internal_type(self):
return "FileField"
def get_db_prep_lookup(self, lookup_type, value):
def get_db_prep_lookup(self, lookup_type, value, connection):
if hasattr(value, 'name'):
value = value.name
return super(FileField, self).get_db_prep_lookup(lookup_type, value)
return call_with_connection(super(FileField, self).get_db_prep_lookup,
lookup_type, value, connection=connection)
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
"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:

View File

@ -117,7 +117,7 @@ class RelatedField(object):
if not cls._meta.abstract:
self.contribute_to_related_class(other, self.related)
def get_db_prep_lookup(self, lookup_type, value):
def get_db_prep_lookup(self, lookup_type, value, connection):
# 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.
@ -137,7 +137,8 @@ class RelatedField(object):
if field:
if lookup_type in ('range', 'in'):
v = [v]
v = field.get_db_prep_lookup(lookup_type, v)
v = call_with_connection(field.get_db_prep_lookup,
lookup_type, v, connection=connection)
if isinstance(v, list):
v = v[0]
return v
@ -720,11 +721,12 @@ class ForeignKey(RelatedField, Field):
return getattr(field_default, self.rel.get_related_field().attname)
return field_default
def get_db_prep_save(self, value):
def get_db_prep_save(self, value, connection):
if value == '' or value == None:
return None
else:
return self.rel.get_related_field().get_db_prep_save(value)
return call_with_connection(self.rel.get_related_field().get_db_prep_save,
value, connection=connection)
def value_to_string(self, obj):
if not obj:

View File

@ -18,7 +18,7 @@ class RelatedObject(object):
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):
def get_db_prep_lookup(self, lookup_type, value, connection):
# Defer to the actual field definition for db prep
return self.field.get_db_prep_lookup(lookup_type, value)

View File

@ -8,6 +8,7 @@ from django.db.models.sql.datastructures import Date
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.query import Query
from django.db.models.sql.where import AND, Constraint
from django.db.utils import call_with_connection
__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
'AggregateQuery']
@ -243,7 +244,8 @@ class UpdateQuery(Query):
if hasattr(val, 'prepare_database_save'):
val = val.prepare_database_save(field)
else:
val = field.get_db_prep_save(val)
val = call_with_connection(field.get_db_prep_save,
val, connection=self.connection)
# Getting the placeholder for the field.
if hasattr(field, 'get_placeholder'):

View File

@ -143,7 +143,8 @@ class WhereNode(tree.Node):
except EmptyShortCircuit:
raise EmptyResultSet
else:
params = Field().get_db_prep_lookup(lookup_type, params_or_value)
params = call_with_connection(Field().get_db_prep_lookup,
lookup_type, params_or_value, connection=connection)
if isinstance(lvalue, tuple):
# A direct database column lookup.
field_sql = self.sql_for_columns(lvalue, qn, connection)
@ -266,13 +267,15 @@ class Constraint(object):
from django.db.models.base import ObjectDoesNotExist
try:
if self.field:
params = self.field.get_db_prep_lookup(lookup_type, value)
params = call_with_connection(self.field.get_db_prep_lookup,
lookup_type, value, connection=connection)
db_type = call_with_connection(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)
params = call_with_connection(Field().get_db_prep_lookup,
lookup_type, value, connection=connection)
db_type = None
except ObjectDoesNotExist:
raise EmptyShortCircuit

View File

@ -3,6 +3,7 @@ Helper functions for creating Form classes from Django models
and database field objects.
"""
from django.db.utils import call_with_connection
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.datastructures import SortedDict
from django.utils.text import get_text_list, capfirst
@ -474,7 +475,8 @@ class BaseModelFormSet(BaseFormSet):
pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
pk = self.data[pk_key]
pk_field = self.model._meta.pk
pk = pk_field.get_db_prep_lookup('exact', pk)
pk = call_with_connection(pk_field.get_db_prep_lookup, 'exact', pk,
connection=self.get_queryset().query.connection)
if isinstance(pk, list):
pk = pk[0]
kwargs['instance'] = self._existing_object(pk)

View File

@ -399,24 +399,27 @@ mentioned earlier. Otherwise :meth:`to_python` won't be called automatically.
Converting Python objects to database values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. method:: get_db_prep_value(self, value)
.. method:: get_db_prep_value(self, value, connection)
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.
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.
For example::
class HandField(models.Field):
# ...
def get_db_prep_value(self, value):
def get_db_prep_value(self, value, connection):
return ''.join([''.join(l) for l in (value.north,
value.east, value.south, value.west)])
.. method:: get_db_prep_save(self, value)
.. 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
@ -450,7 +453,7 @@ correct value.
Preparing values for use in database lookups
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. method:: get_db_prep_lookup(self, lookup_type, value)
.. method:: get_db_prep_lookup(self, lookup_type, value, connection)
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

View File

@ -44,8 +44,9 @@ class DecimalFieldTests(django.test.TestCase):
self.assertEqual(f._format(None), None)
def test_get_db_prep_lookup(self):
from django.db import connection
f = models.DecimalField(max_digits=5, decimal_places=1)
self.assertEqual(f.get_db_prep_lookup('exact', None), [None])
self.assertEqual(f.get_db_prep_lookup('exact', None, connection), [None])
def test_filter_with_strings(self):
"""
@ -98,13 +99,14 @@ class DateTimeFieldTests(unittest.TestCase):
class BooleanFieldTests(unittest.TestCase):
def _test_get_db_prep_lookup(self, f):
self.assertEqual(f.get_db_prep_lookup('exact', True), [True])
self.assertEqual(f.get_db_prep_lookup('exact', '1'), [True])
self.assertEqual(f.get_db_prep_lookup('exact', 1), [True])
self.assertEqual(f.get_db_prep_lookup('exact', False), [False])
self.assertEqual(f.get_db_prep_lookup('exact', '0'), [False])
self.assertEqual(f.get_db_prep_lookup('exact', 0), [False])
self.assertEqual(f.get_db_prep_lookup('exact', None), [None])
from django.db import connection
self.assertEqual(f.get_db_prep_lookup('exact', True, connection), [True])
self.assertEqual(f.get_db_prep_lookup('exact', '1', connection), [True])
self.assertEqual(f.get_db_prep_lookup('exact', 1, connection), [True])
self.assertEqual(f.get_db_prep_lookup('exact', False, connection), [False])
self.assertEqual(f.get_db_prep_lookup('exact', '0', connection), [False])
self.assertEqual(f.get_db_prep_lookup('exact', 0, connection), [False])
self.assertEqual(f.get_db_prep_lookup('exact', None, connection), [None])
def test_booleanfield_get_db_prep_lookup(self):
self._test_get_db_prep_lookup(models.BooleanField())