summaryrefslogtreecommitdiff
path: root/django/db/models/fields/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/models/fields/__init__.py')
-rw-r--r--django/db/models/fields/__init__.py134
1 files changed, 97 insertions, 37 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 1be0bc353c..b70f320df3 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -8,6 +8,7 @@ import django.utils.copycompat as copy
from django.db import connection
from django.db.models import signals
+from django.db.models.fields.subclassing import LegacyConnection
from django.db.models.query_utils import QueryWrapper
from django.dispatch import dispatcher
from django.conf import settings
@@ -47,6 +48,9 @@ class FieldDoesNotExist(Exception):
# getattr(obj, opts.pk.attname)
class Field(object):
+ """Base class for all field types"""
+ __metaclass__ = LegacyConnection
+
# Designates whether empty strings fundamentally are allowed at the
# database level.
empty_strings_allowed = True
@@ -123,10 +127,10 @@ class Field(object):
"""
return value
- def db_type(self):
+ def db_type(self, connection):
"""
- Returns the database column data type for this field, taking into
- account the DATABASE_ENGINE setting.
+ Returns the database column data type for this field, for the provided
+ connection.
"""
# The default implementation of this method looks at the
# backend-specific DATA_TYPES dictionary, looking up the field by its
@@ -183,21 +187,56 @@ 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_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):
+ 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 self.get_db_prep_value(value, connection=connection, prepared=False)
+
+ 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")
- def get_db_prep_lookup(self, lookup_type, value):
+ 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,15 +245,15 @@ class Field(object):
if hasattr(value, 'as_sql'):
sql, params = value.as_sql()
else:
- sql, params = value._as_sql()
+ 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)]
+ return [self.get_db_prep_value(value, connection=connection, prepared=prepared)]
elif lookup_type in ('range', 'in'):
- return [self.get_db_prep_value(v) 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':
@@ -226,18 +265,11 @@ class Field(object):
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 has_default(self):
"Returns a boolean of whether this field has a default value."
return self.default is not NOT_PROVIDED
@@ -346,6 +378,7 @@ class Field(object):
class AutoField(Field):
description = ugettext_lazy("Integer")
+
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
@@ -361,7 +394,7 @@ class AutoField(Field):
raise exceptions.ValidationError(
_("This value must be an integer."))
- def get_db_prep_value(self, value):
+ def get_prep_value(self, value):
if value is None:
return None
return int(value)
@@ -394,16 +427,16 @@ 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_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)
+ return super(BooleanField, self).get_prep_lookup(lookup_type, value)
- def get_db_prep_value(self, value):
+ def get_prep_value(self, value):
if value is None:
return None
return bool(value)
@@ -421,6 +454,7 @@ class BooleanField(Field):
class CharField(Field):
description = ugettext_lazy("String (up to %(max_length)s)")
+
def get_internal_type(self):
return "CharField"
@@ -443,6 +477,7 @@ class CharField(Field):
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
description = ugettext_lazy("Comma-separated integers")
+
def formfield(self, **kwargs):
defaults = {
'form_class': forms.RegexField,
@@ -459,6 +494,7 @@ ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')
class DateField(Field):
description = ugettext_lazy("Date (without time)")
+
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
@@ -509,16 +545,21 @@ 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_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)
+ return int(value)
+ return super(DateField, self).get_prep_lookup(lookup_type, value)
- def get_db_prep_value(self, value):
+ 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 value_to_string(self, obj):
val = self._get_val_from_obj(obj)
@@ -535,6 +576,7 @@ class DateField(Field):
class DateTimeField(DateField):
description = ugettext_lazy("Date (with time)")
+
def get_internal_type(self):
return "DateTimeField"
@@ -575,9 +617,14 @@ 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_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)
@@ -632,11 +679,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_prep_value(self, value):
return self.to_python(value)
def formfield(self, **kwargs):
@@ -661,6 +708,7 @@ class EmailField(CharField):
class FilePathField(Field):
description = ugettext_lazy("File path")
+
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
kwargs['max_length'] = kwargs.get('max_length', 100)
@@ -683,7 +731,7 @@ class FloatField(Field):
empty_strings_allowed = False
description = ugettext_lazy("Floating point number")
- def get_db_prep_value(self, value):
+ def get_prep_value(self, value):
if value is None:
return None
return float(value)
@@ -708,7 +756,8 @@ class FloatField(Field):
class IntegerField(Field):
empty_strings_allowed = False
description = ugettext_lazy("Integer")
- def get_db_prep_value(self, value):
+
+ def get_prep_value(self, value):
if value is None:
return None
return int(value)
@@ -776,16 +825,16 @@ 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_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)
+ return super(NullBooleanField, self).get_prep_lookup(lookup_type, value)
- def get_db_prep_value(self, value):
+ def get_prep_value(self, value):
if value is None:
return None
return bool(value)
@@ -801,6 +850,7 @@ class NullBooleanField(Field):
class PositiveIntegerField(IntegerField):
description = ugettext_lazy("Integer")
+
def get_internal_type(self):
return "PositiveIntegerField"
@@ -838,11 +888,13 @@ class SlugField(CharField):
class SmallIntegerField(IntegerField):
description = ugettext_lazy("Integer")
+
def get_internal_type(self):
return "SmallIntegerField"
class TextField(Field):
description = ugettext_lazy("Text")
+
def get_internal_type(self):
return "TextField"
@@ -853,6 +905,7 @@ class TextField(Field):
class TimeField(Field):
description = ugettext_lazy("Time")
+
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
@@ -907,9 +960,14 @@ class TimeField(Field):
else:
return super(TimeField, self).pre_save(model_instance, add)
- def get_db_prep_value(self, value):
+ 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)
@@ -926,6 +984,7 @@ class TimeField(Field):
class URLField(CharField):
description = ugettext_lazy("URL")
+
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 200)
self.verify_exists = verify_exists
@@ -938,6 +997,7 @@ class URLField(CharField):
class XMLField(TextField):
description = ugettext_lazy("XML text")
+
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
self.schema_path = schema_path
Field.__init__(self, verbose_name, name, **kwargs)