Skip to content
Browse files

Replaced value_to_db_auto with a different solution allowing to use n…

…on-int AutoFields. This solution doesn't weaken validation so much.
  • Loading branch information...
1 parent d470c01 commit d8b48a919160bb0e3fbc883ad4ca3192a7796579 @wrwrwr wrwrwr committed Mar 2, 2012
Showing with 45 additions and 21 deletions.
  1. +0 −9 django/db/backends/__init__.py
  2. +45 −12 django/db/models/fields/__init__.py
View
9 django/db/backends/__init__.py
@@ -673,15 +673,6 @@ def prep_for_like_query(self, x):
# need not necessarily be implemented using "LIKE" in the backend.
prep_for_iexact_query = prep_for_like_query
- def value_to_db_auto(self, value):
- """
- Transform a value to an object compatible with the auto field required
- by the backend driver for auto columns.
- """
- if value is None:
- return None
- return int(value)
-
def value_to_db_date(self, value):
"""
Transform a date value to an object compatible with what is expected
View
57 django/db/models/fields/__init__.py
@@ -448,11 +448,44 @@ def value_from_object(self, obj):
return getattr(obj, self.attname)
class AutoField(Field):
- description = _("Integer")
+ description = _("Automatic key")
+
+ # Values for the field are cast to this type. It may be overriden
+ # by back-ends that don't want to support integers as automatic
+ # keys (due to a database being unable to generate unique integers
+ # efficiently or offering hard-to-guess keys of a different type).
+ # Why do we need a fixed type at all? Because, for example, a model
+ # created with an int(1) key can be looked up using string('1').
+ # This is not a flaw -- values arrive as strings in requests and
+ # "untyped" field wouldn't know whether a string is really a string
+ # or if it should be cast to another type (unless you'd pass around
+ # values reinforced by encoded type, but that's rather not worth
+ # the hassle).
+ # Why not do this within the database layer? Because it results in
+ # weakening of validation and impacts processing field values
+ # elsewhere (e.g. their serialization).
+ # Usage: `AutoField.value_type = long / unicode` once (e.g. in
+ # back-end compiler's global scope).
+ _value_type = None
+
+ class __metaclass__(Field.__metaclass__):
+ def get_value_type(cls):
+ if cls._value_type is None:
+ cls._value_type = int
+ return cls._value_type
+
+ def set_value_type(cls, new_type):
+ if cls._value_type is not None and cls._value_type != new_type:
+ raise exceptions.ImproperlyConfigured(
+ "Cannot use back-ends with different %s value "
+ "types together." % str(cls))
+ cls._value_type = new_type
+
+ value_type = property(get_value_type, set_value_type)
empty_strings_allowed = False
default_error_messages = {
- 'invalid': _(u'This value must be an integer.'),
+ 'invalid': _(u'This value must be of the %s type.'),
}
def __init__(self, *args, **kwargs):
assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
@@ -463,21 +496,21 @@ def get_internal_type(self):
return "AutoField"
def to_python(self, value):
- if not (value is None or isinstance(value, (basestring, int, long))):
- raise exceptions.ValidationError(self.error_messages['invalid'])
- return value
+ if value is None:
+ return value
+ try:
+ return self.__class__.value_type(value)
+ except (TypeError, ValueError):
+ raise exceptions.ValidationError(self.error_messages['invalid'] %
+ str(self.__class__.value_type))
def validate(self, value, model_instance):
pass
def get_prep_value(self, value):
- return value
-
- def get_db_prep_value(self, value, connection, prepared=False):
- # Casts AutoField into the format expected by the backend
- if not prepared:
- value = self.get_prep_value(value)
- return connection.ops.value_to_db_auto(value)
+ if value is None:
+ return None
+ return self.__class__.value_type(value)
def contribute_to_class(self, cls, name):
assert not cls._meta.has_auto_field, "A model can't have more than one AutoField."

0 comments on commit d8b48a9

Please sign in to comment.
Something went wrong with that request. Please try again.