summaryrefslogtreecommitdiff
path: root/django/db/models/fields
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/models/fields')
-rw-r--r--django/db/models/fields/__init__.py78
-rw-r--r--django/db/models/fields/related.py74
2 files changed, 120 insertions, 32 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 383000d0aa..d16bb9d15d 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -228,7 +228,11 @@ class Field(object):
value = int(value)
except ValueError:
raise ValueError("The __year lookup type requires an integer argument")
- return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.999999' % value]
+ if settings.DATABASE_ENGINE == 'sqlite3':
+ first = '%s-01-01'
+ else:
+ first = '%s-01-01 00:00:00'
+ return [first % value, '%s-12-31 23:59:59.999999' % value]
raise TypeError("Field has invalid lookup: %s" % lookup_type)
def has_default(self):
@@ -451,6 +455,9 @@ class BooleanField(Field):
kwargs['blank'] = True
Field.__init__(self, *args, **kwargs)
+ def get_internal_type(self):
+ return "BooleanField"
+
def to_python(self, value):
if value in (True, False): return value
if value in ('t', 'True', '1'): return True
@@ -469,6 +476,9 @@ class CharField(Field):
def get_manipulator_field_objs(self):
return [oldforms.TextField]
+ def get_internal_type(self):
+ return "CharField"
+
def to_python(self, value):
if isinstance(value, basestring):
return value
@@ -499,6 +509,9 @@ class DateField(Field):
kwargs['blank'] = True
Field.__init__(self, verbose_name, name, **kwargs)
+ def get_internal_type(self):
+ return "DateField"
+
def to_python(self, value):
if value is None:
return value
@@ -568,6 +581,9 @@ class DateField(Field):
return super(DateField, self).formfield(**defaults)
class DateTimeField(DateField):
+ def get_internal_type(self):
+ return "DateTimeField"
+
def to_python(self, value):
if value is None:
return value
@@ -638,6 +654,9 @@ class DecimalField(Field):
self.max_digits, self.decimal_places = max_digits, decimal_places
Field.__init__(self, verbose_name, name, **kwargs)
+ def get_internal_type(self):
+ return "DecimalField"
+
def to_python(self, value):
if value is None:
return value
@@ -697,9 +716,6 @@ class EmailField(CharField):
kwargs['max_length'] = kwargs.get('max_length', 75)
CharField.__init__(self, *args, **kwargs)
- def get_internal_type(self):
- return "CharField"
-
def get_manipulator_field_objs(self):
return [oldforms.EmailField]
@@ -717,6 +733,9 @@ class FileField(Field):
kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
+ def get_internal_type(self):
+ return "FileField"
+
def get_db_prep_save(self, value):
"Returns field's value prepared for saving into a database."
# Need to convert UploadedFile objects provided via a form to unicode for database insertion
@@ -826,12 +845,18 @@ class FilePathField(Field):
def get_manipulator_field_objs(self):
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
+ def get_internal_type(self):
+ return "FilePathField"
+
class FloatField(Field):
empty_strings_allowed = False
def get_manipulator_field_objs(self):
return [oldforms.FloatField]
+ def get_internal_type(self):
+ return "FloatField"
+
def formfield(self, **kwargs):
defaults = {'form_class': forms.FloatField}
defaults.update(kwargs)
@@ -854,6 +879,9 @@ class ImageField(FileField):
if not self.height_field:
setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
+ def get_internal_type(self):
+ return "ImageField"
+
def save_file(self, new_data, new_object, original_object, change, rel, save=True):
FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
# If the image has height and/or width field(s) and they haven't
@@ -876,6 +904,9 @@ class IntegerField(Field):
def get_manipulator_field_objs(self):
return [oldforms.IntegerField]
+ def get_internal_type(self):
+ return "IntegerField"
+
def formfield(self, **kwargs):
defaults = {'form_class': forms.IntegerField}
defaults.update(kwargs)
@@ -890,6 +921,9 @@ class IPAddressField(Field):
def get_manipulator_field_objs(self):
return [oldforms.IPAddressField]
+ def get_internal_type(self):
+ return "IPAddressField"
+
def validate(self, field_data, all_data):
validators.isValidIPAddress4(field_data, None)
@@ -904,6 +938,9 @@ class NullBooleanField(Field):
kwargs['null'] = True
Field.__init__(self, *args, **kwargs)
+ def get_internal_type(self):
+ return "NullBooleanField"
+
def to_python(self, value):
if value in (None, True, False): return value
if value in ('None'): return None
@@ -923,6 +960,9 @@ class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.PhoneNumberField]
+ def get_internal_type(self):
+ return "PhoneNumberField"
+
def validate(self, field_data, all_data):
validators.isValidPhone(field_data, all_data)
@@ -936,6 +976,9 @@ class PositiveIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.PositiveIntegerField]
+ def get_internal_type(self):
+ return "PositiveIntegerField"
+
def formfield(self, **kwargs):
defaults = {'min_value': 0}
defaults.update(kwargs)
@@ -945,6 +988,9 @@ class PositiveSmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.PositiveSmallIntegerField]
+ def get_internal_type(self):
+ return "PositiveSmallIntegerField"
+
def formfield(self, **kwargs):
defaults = {'min_value': 0}
defaults.update(kwargs)
@@ -959,14 +1005,23 @@ class SlugField(CharField):
kwargs['db_index'] = True
super(SlugField, self).__init__(*args, **kwargs)
+ def get_internal_type(self):
+ return "SlugField"
+
class SmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.SmallIntegerField]
+ def get_internal_type(self):
+ return "SmallIntegerField"
+
class TextField(Field):
def get_manipulator_field_objs(self):
return [oldforms.LargeTextField]
+ def get_internal_type(self):
+ return "TextField"
+
def formfield(self, **kwargs):
defaults = {'widget': forms.Textarea}
defaults.update(kwargs)
@@ -980,6 +1035,9 @@ class TimeField(Field):
kwargs['editable'] = False
Field.__init__(self, verbose_name, name, **kwargs)
+ def get_internal_type(self):
+ return "TimeField"
+
def get_db_prep_lookup(self, lookup_type, value):
if settings.DATABASE_ENGINE == 'oracle':
# Oracle requires a date in order to parse.
@@ -1044,9 +1102,6 @@ class URLField(CharField):
def get_manipulator_field_objs(self):
return [oldforms.URLField]
- def get_internal_type(self):
- return "CharField"
-
def formfield(self, **kwargs):
defaults = {'form_class': forms.URLField, 'verify_exists': self.verify_exists}
defaults.update(kwargs)
@@ -1056,6 +1111,9 @@ class USStateField(Field):
def get_manipulator_field_objs(self):
return [oldforms.USStateField]
+ def get_internal_type(self):
+ return "USStateField"
+
def formfield(self, **kwargs):
from django.contrib.localflavor.us.forms import USStateSelect
defaults = {'widget': USStateSelect}
@@ -1067,9 +1125,6 @@ class XMLField(TextField):
self.schema_path = schema_path
Field.__init__(self, verbose_name, name, **kwargs)
- def get_internal_type(self):
- return "TextField"
-
def get_manipulator_field_objs(self):
return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
@@ -1080,8 +1135,5 @@ class OrderingField(IntegerField):
kwargs['null'] = True
IntegerField.__init__(self, **kwargs )
- def get_internal_type(self):
- return "IntegerField"
-
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
return [oldforms.HiddenField(name_prefix + self.name)]
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 2ccf1e3dd0..ab23fee210 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -23,26 +23,64 @@ RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
pending_lookups = {}
-def add_lookup(rel_cls, field):
- name = field.rel.to
- module = rel_cls.__module__
- key = (module, name)
- # Has the model already been loaded?
- # If so, resolve the string reference right away
- model = get_model(rel_cls._meta.app_label, field.rel.to, False)
+def add_lazy_relation(cls, field, relation):
+ """
+ Adds a lookup on ``cls`` when a related field is defined using a string,
+ i.e.::
+
+ class MyModel(Model):
+ fk = ForeignKey("AnotherModel")
+
+ This string can be:
+
+ * RECURSIVE_RELATIONSHIP_CONSTANT (i.e. "self") to indicate a recursive
+ relation.
+
+ * The name of a model (i.e "AnotherModel") to indicate another model in
+ the same app.
+
+ * An app-label and model name (i.e. "someapp.AnotherModel") to indicate
+ another model in a different app.
+
+ If the other model hasn't yet been loaded -- almost a given if you're using
+ lazy relationships -- then the relation won't be set up until the
+ class_prepared signal fires at the end of model initialization.
+ """
+ # Check for recursive relations
+ if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
+ app_label = cls._meta.app_label
+ model_name = cls.__name__
+
+ else:
+ # Look for an "app.Model" relation
+ try:
+ app_label, model_name = relation.split(".")
+ except ValueError:
+ # If we can't split, assume a model in current app
+ app_label = cls._meta.app_label
+ model_name = relation
+
+ # Try to look up the related model, and if it's already loaded resolve the
+ # string right away. If get_model returns None, it means that the related
+ # model isn't loaded yet, so we need to pend the relation until the class
+ # is prepared.
+ model = get_model(app_label, model_name, False)
if model:
field.rel.to = model
- field.do_related_class(model, rel_cls)
+ field.do_related_class(model, cls)
else:
- # Mark the related field for later lookup
- pending_lookups.setdefault(key, []).append((rel_cls, field))
-
+ key = (app_label, model_name)
+ value = (cls, field)
+ pending_lookups.setdefault(key, []).append(value)
+
def do_pending_lookups(sender):
- other_cls = sender
- key = (other_cls.__module__, other_cls.__name__)
- for rel_cls, field in pending_lookups.setdefault(key, []):
- field.rel.to = other_cls
- field.do_related_class(other_cls, rel_cls)
+ """
+ Handle any pending relations to the sending model. Sent from class_prepared.
+ """
+ key = (sender._meta.app_label, sender.__name__)
+ for cls, field in pending_lookups.pop(key, []):
+ field.rel.to = sender
+ field.do_related_class(sender, cls)
dispatcher.connect(do_pending_lookups, signal=signals.class_prepared)
@@ -66,9 +104,7 @@ class RelatedField(object):
sup.contribute_to_class(cls, name)
other = self.rel.to
if isinstance(other, basestring):
- if other == RECURSIVE_RELATIONSHIP_CONSTANT:
- self.rel.to = cls.__name__
- add_lookup(cls, self)
+ add_lazy_relation(cls, self, other)
else:
self.do_related_class(other, cls)