diff --git a/django/core/management.py b/django/core/management.py
index a8931de544..6314ca6ac0 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -264,7 +264,7 @@ database_check.args = APP_ARGS
def get_admin_index(mod):
"Returns admin-index template snippet (in list form) for the given module."
- from django.core import meta
+ from django.utils.text import capfirst
output = []
app_label = mod._MODELS[0]._meta.app_label
output.append('{%% if perms.%s %%}' % app_label)
@@ -274,7 +274,7 @@ def get_admin_index(mod):
output.append(MODULE_TEMPLATE % {
'app': app_label,
'mod': klass._meta.module_name,
- 'name': meta.capfirst(klass._meta.verbose_name_plural),
+ 'name': capfirst(klass._meta.verbose_name_plural),
'addperm': klass._meta.get_add_permission(),
'changeperm': klass._meta.get_change_permission(),
})
diff --git a/django/core/meta.py b/django/core/meta/__init__.py
similarity index 71%
rename from django/core/meta.py
rename to django/core/meta/__init__.py
index 98ce768f72..20b5ee6863 100644
--- a/django/core/meta.py
+++ b/django/core/meta/__init__.py
@@ -1,30 +1,20 @@
+from django.conf import settings
from django.core import formfields, validators
from django.core import db
from django.core.exceptions import ObjectDoesNotExist
-from django.conf import settings
+from django.core.meta.fields import *
+from django.utils.functional import curry
+from django.utils.text import capfirst
import copy, datetime, os, re, sys, types
-# The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
-BLANK_CHOICE_DASH = [("", "---------")]
-BLANK_CHOICE_NONE = [("", "None")]
-
# Admin stages.
ADD, CHANGE, BOTH = 1, 2, 3
-# Values for Relation.edit_inline_type.
-TABULAR, STACKED = 1, 2
-
-# Values for filter_interface.
-HORIZONTAL, VERTICAL = 1, 2
-
-# Random entropy string used by "default" param.
-NOT_PROVIDED = 'oijpwojefiojpanv'
-
# Size of each "chunk" for get_iterator calls.
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
-# Prefix (in python path style) to location of models.
+# Prefix (in Python path style) to location of models.
MODEL_PREFIX = 'django.models'
# Methods on models with the following prefix will be removed and
@@ -37,21 +27,10 @@ MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_'
LOOKUP_SEPARATOR = '__'
-RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
-
####################
# HELPER FUNCTIONS #
####################
-# capitalizes first letter of string
-capfirst = lambda x: x and x[0].upper() + x[1:]
-
-# prepares a value for use in a LIKE query
-prep_for_like_query = lambda x: str(x).replace("%", "\%").replace("_", "\_")
-
-# returns the
class for a given radio_admin value
-get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
-
# Django currently supports two forms of ordering.
# Form 1 (deprecated) example:
# order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM'))
@@ -82,11 +61,6 @@ def orderlist2sql(order_list, prefix=''):
output.append('%s%s ASC' % (prefix, f))
return ', '.join(output)
-def curry(*args, **kwargs):
- def _curried(*moreargs, **morekwargs):
- return args[0](*(args[1:]+moreargs), **dict(kwargs.items() + morekwargs.items()))
- return _curried
-
def get_module(app_label, module_name):
return __import__('%s.%s.%s' % (MODEL_PREFIX, app_label, module_name), '', '', [''])
@@ -1529,16 +1503,6 @@ def manipulator_save(opts, klass, add, change, self, new_data):
getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
return new_object
-def manipulator_validator_unique(f, opts, self, field_data, all_data):
- "Validates that the value is unique for this field."
- try:
- old_obj = opts.get_model_module().get_object(**{'%s__exact' % f.name: field_data})
- except ObjectDoesNotExist:
- return
- if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
- return
- raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
-
def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_list]
@@ -1583,641 +1547,3 @@ def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_t
format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y'
raise validators.ValidationError, "Please enter a different %s. The one you entered is already being used for %s." % \
(from_field.verbose_name, date_val.strftime(format_string))
-
-def manipulator_valid_rel_key(f, self, field_data, all_data):
- "Validates that the value is a valid foreign key"
- mod = f.rel.to.get_model_module()
- try:
- mod.get_object(**{'id__iexact': field_data})
- except ObjectDoesNotExist:
- raise validators.ValidationError, "Please enter a valid %s." % f.verbose_name
-
-####################
-# FIELDS #
-####################
-
-class Field(object):
-
- # Designates whether empty strings fundamentally are allowed at the
- # database level.
- empty_strings_allowed = True
-
- def __init__(self, name, verbose_name=None, primary_key=False,
- maxlength=None, unique=False, blank=False, null=False, db_index=None,
- core=False, rel=None, default=NOT_PROVIDED, editable=True,
- prepopulate_from=None, unique_for_date=None, unique_for_month=None,
- unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
- help_text=''):
- self.name = name
- self.verbose_name = verbose_name or name.replace('_', ' ')
- self.primary_key = primary_key
- self.maxlength, self.unique = maxlength, unique
- self.blank, self.null = blank, null
- self.core, self.rel, self.default = core, rel, default
- self.editable = editable
- self.validator_list = validator_list or []
- self.prepopulate_from = prepopulate_from
- self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
- self.unique_for_year = unique_for_year
- self.choices = choices or []
- self.radio_admin = radio_admin
- self.help_text = help_text
- if rel and isinstance(rel, ManyToMany):
- self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
-
- # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
- if db_index is None:
- if isinstance(rel, OneToOne) or isinstance(rel, ManyToOne):
- self.db_index = True
- else:
- self.db_index = False
- else:
- self.db_index = db_index
-
- def pre_save(self, obj, value, add):
- """
- Hook for altering the object obj based on the value of this field and
- and on the add/change status.
- """
- pass
-
- def get_db_prep_save(self, value, add):
- "Returns field's value prepared for saving into a database."
- return value
-
- def get_db_prep_lookup(self, lookup_type, value):
- "Returns field's value prepared for database lookup."
- if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'month', 'day'):
- return [value]
- elif lookup_type in ('range', 'in'):
- return value
- elif lookup_type == 'year':
- return ['%s-01-01' % value, '%s-12-31' % value]
- elif lookup_type in ('contains', 'icontains'):
- return ["%%%s%%" % prep_for_like_query(value)]
- elif lookup_type == 'iexact':
- return [prep_for_like_query(value)]
- elif lookup_type in ('startswith', 'istartswith'):
- return ["%s%%" % prep_for_like_query(value)]
- elif lookup_type in ('endswith', 'iendswith'):
- return ["%%%s" % prep_for_like_query(value)]
- elif lookup_type == 'isnull':
- return []
- 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 != NOT_PROVIDED
-
- def get_default(self):
- "Returns the default value for this field."
- if self.default != NOT_PROVIDED:
- if hasattr(self.default, '__get_value__'):
- return self.default.__get_value__()
- return self.default
- if self.null:
- return None
- return ""
-
- def get_manipulator_field_names(self, name_prefix):
- """
- Returns a list of field names that this object adds to the manipulator.
- """
- return [name_prefix + self.name]
-
- def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
- """
- Returns a list of formfields.FormField instances for this field. It
- calculates the choices at runtime, not at compile time.
-
- name_prefix is a prefix to prepend to the "field_name" argument.
- rel is a boolean specifying whether this field is in a related context.
- """
- params = {'validator_list': self.validator_list[:]}
- if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
- params['maxlength'] = self.maxlength
- if isinstance(self.rel, ManyToOne):
- if self.rel.raw_id_admin:
- field_objs = self.get_manipulator_field_objs()
- params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
- else:
- if self.radio_admin:
- field_objs = [formfields.RadioSelectField]
- params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
- params['ul_class'] = get_ul_class(self.radio_admin)
- else:
- if self.null:
- field_objs = [formfields.NullSelectField]
- else:
- field_objs = [formfields.SelectField]
- params['choices'] = self.get_choices()
- elif self.choices:
- if self.radio_admin:
- field_objs = [formfields.RadioSelectField]
- params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
- params['ul_class'] = get_ul_class(self.radio_admin)
- else:
- field_objs = [formfields.SelectField]
- params['choices'] = self.get_choices()
- else:
- field_objs = self.get_manipulator_field_objs()
-
- # Add the "unique" validator(s).
- for field_name_list in opts.unique_together:
- if field_name_list[0] == self.name:
- params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
-
- # Add the "unique for..." validator(s).
- if self.unique_for_date:
- params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
- if self.unique_for_month:
- params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
- if self.unique_for_year:
- params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
- if self.unique:
- params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
-
- # Only add is_required=True if the field cannot be blank. Primary keys
- # are a special case, and fields in a related context should set this
- # as False, because they'll be caught by a separate validator --
- # RequiredIfOtherFieldGiven.
- params['is_required'] = not self.blank and not self.primary_key and not rel
-
- # If this field is in a related context, check whether any other fields
- # in the related object have core=True. If so, add a validator --
- # RequiredIfOtherFieldsGiven -- to this FormField.
- if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
- # First, get the core fields, if any.
- core_field_names = []
- for f in opts.fields:
- if f.core and f != self:
- core_field_names.extend(f.get_manipulator_field_names(name_prefix))
- # Now, if there are any, add the validator to this FormField.
- if core_field_names:
- params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, "This field is required."))
-
- # BooleanFields (CheckboxFields) are a special case. They don't take
- # is_required or validator_list.
- if isinstance(self, BooleanField):
- del params['validator_list'], params['is_required']
-
- # Finally, add the field_names.
- field_names = self.get_manipulator_field_names(name_prefix)
- return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
-
- def get_manipulator_new_data(self, new_data, rel=False):
- """
- Given the full new_data dictionary (from the manipulator), returns this
- field's data.
- """
- if rel:
- return new_data.get(self.name, [self.get_default()])[0]
- else:
- val = new_data.get(self.name, self.get_default())
- if not self.empty_strings_allowed and val == '' and self.null:
- val = None
- return val
-
- def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
- "Returns a list of tuples used as SelectField choices for this field."
- first_choice = include_blank and blank_choice or []
- if self.choices:
- return first_choice + list(self.choices)
- rel_obj = self.rel.to
- return first_choice + [(getattr(x, rel_obj.pk.name), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
-
-class AutoField(Field):
- empty_strings_allowed = False
- def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
- if not rel:
- return [] # Don't add a FormField unless it's in a related context.
- return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
-
- def get_manipulator_field_objs(self):
- return [formfields.HiddenField]
-
- def get_manipulator_new_data(self, new_data, rel=False):
- if not rel:
- return None
- return Field.get_manipulator_new_data(self, new_data, rel)
-
-class BooleanField(Field):
- def __init__(self, *args, **kwargs):
- kwargs['blank'] = True
- Field.__init__(self, *args, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.CheckboxField]
-
-class CharField(Field):
- def get_manipulator_field_objs(self):
- return [formfields.TextField]
-
-class CommaSeparatedIntegerField(CharField):
- def get_manipulator_field_objs(self):
- return [formfields.CommaSeparatedIntegerField]
-
-class DateField(Field):
- empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
- self.auto_now, self.auto_now_add = auto_now, auto_now_add
- if auto_now or auto_now_add:
- kwargs['editable'] = False
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_db_prep_lookup(self, lookup_type, value):
- if lookup_type == 'range':
- value = [str(v) for v in value]
- else:
- value = str(value)
- return Field.get_db_prep_lookup(self, lookup_type, value)
-
- def pre_save(self, obj, value, add):
- if self.auto_now or (self.auto_now_add and add):
- setattr(obj, self.name, datetime.datetime.now())
-
- def get_db_prep_save(self, value, add):
- # Casts dates into string format for entry into database.
- if value is not None:
- value = value.strftime('%Y-%m-%d')
- return Field.get_db_prep_save(self, value, add)
-
- def get_manipulator_field_objs(self):
- return [formfields.DateField]
-
-class DateTimeField(DateField):
- def get_db_prep_save(self, value, add):
- # Casts dates into string format for entry into database.
- if value is not None:
- value = value.strftime('%Y-%m-%d %H:%M:%S')
- return Field.get_db_prep_save(self, value, add)
-
- def get_manipulator_field_objs(self):
- return [formfields.DateField, formfields.TimeField]
-
- def get_manipulator_field_names(self, name_prefix):
- return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
-
- def get_manipulator_new_data(self, new_data, rel=False):
- date_field, time_field = self.get_manipulator_field_names('')
- if rel:
- d = new_data.get(date_field, [None])[0]
- t = new_data.get(time_field, [None])[0]
- else:
- d = new_data.get(date_field, None)
- t = new_data.get(time_field, None)
- if d is not None and t is not None:
- return datetime.datetime.combine(d, t)
- return self.get_default()
-
-class EmailField(Field):
- def get_manipulator_field_objs(self):
- return [formfields.EmailField]
-
-class FileField(Field):
- def __init__(self, name, verbose_name=None, upload_to='', **kwargs):
- self.upload_to = upload_to
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
- field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
-
- if not self.blank:
- if rel:
- # This validator makes sure FileFields work in a related context.
- class RequiredFileField:
- def __init__(self, other_field_names, other_file_field_name):
- self.other_field_names = other_field_names
- self.other_file_field_name = other_file_field_name
- self.always_test = True
- def __call__(self, field_data, all_data):
- if not all_data.get(self.other_file_field_name, False):
- c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, "This field is required.")
- c(field_data, all_data)
- # First, get the core fields, if any.
- core_field_names = []
- for f in opts.fields:
- if f.core and f != self:
- core_field_names.extend(f.get_manipulator_field_names(name_prefix))
- # Now, if there are any, add the validator to this FormField.
- if core_field_names:
- field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
- else:
- v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, "This field is required.")
- v.always_test = True
- field_list[0].validator_list.append(v)
- field_list[0].is_required = field_list[1].is_required = False
-
- # If the raw path is passed in, validate it's under the MEDIA_ROOT.
- def isWithinMediaRoot(field_data, all_data):
- f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
- if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
- raise validators.ValidationError, "Enter a valid filename."
- field_list[1].validator_list.append(isWithinMediaRoot)
- return field_list
-
- def get_manipulator_field_objs(self):
- return [formfields.FileUploadField, formfields.HiddenField]
-
- def get_manipulator_field_names(self, name_prefix):
- return [name_prefix + self.name + '_file', name_prefix + self.name]
-
- def save_file(self, new_data, new_object, original_object, change, rel):
- upload_field_name = self.get_manipulator_field_names('')[0]
- if new_data.get(upload_field_name, False):
- if rel:
- getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
- else:
- getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
-
- def get_directory_name(self):
- return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
-
- def get_filename(self, filename):
- from django.utils.text import get_valid_filename
- f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
- return os.path.normpath(f)
-
-class FloatField(Field):
- empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, max_digits=None, decimal_places=None, **kwargs):
- self.max_digits, self.decimal_places = max_digits, decimal_places
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
-
-class ImageField(FileField):
- def __init__(self, name, verbose_name=None, width_field=None, height_field=None, **kwargs):
- self.width_field, self.height_field = width_field, height_field
- FileField.__init__(self, name, verbose_name, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.ImageUploadField, formfields.HiddenField]
-
- def save_file(self, new_data, new_object, original_object, change, rel):
- FileField.save_file(self, new_data, new_object, original_object, change, rel)
- # If the image has height and/or width field(s) and they haven't
- # changed, set the width and/or height field(s) back to their original
- # values.
- if change and (self.width_field or self.height_field):
- if self.width_field:
- setattr(new_object, self.width_field, getattr(original_object, self.width_field))
- if self.height_field:
- setattr(new_object, self.height_field, getattr(original_object, self.height_field))
- new_object.save()
-
-class IntegerField(Field):
- empty_strings_allowed = False
- def get_manipulator_field_objs(self):
- return [formfields.IntegerField]
-
-class IPAddressField(Field):
- def __init__(self, *args, **kwargs):
- kwargs['maxlength'] = 15
- Field.__init__(self, *args, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.IPAddressField]
-
-class NullBooleanField(Field):
- def __init__(self, *args, **kwargs):
- kwargs['null'] = True
- Field.__init__(self, *args, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.NullBooleanField]
-
-class PhoneNumberField(IntegerField):
- def get_manipulator_field_objs(self):
- return [formfields.PhoneNumberField]
-
-class PositiveIntegerField(IntegerField):
- def get_manipulator_field_objs(self):
- return [formfields.PositiveIntegerField]
-
-class PositiveSmallIntegerField(IntegerField):
- def get_manipulator_field_objs(self):
- return [formfields.PositiveSmallIntegerField]
-
-class SlugField(Field):
- def __init__(self, *args, **kwargs):
- kwargs['maxlength'] = 50
- kwargs.setdefault('validator_list', []).append(validators.isAlphaNumeric)
- # Set db_index=True unless it's been set manually.
- if not kwargs.has_key('db_index'):
- kwargs['db_index'] = True
- Field.__init__(self, *args, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.TextField]
-
-class SmallIntegerField(IntegerField):
- def get_manipulator_field_objs(self):
- return [formfields.SmallIntegerField]
-
-class TextField(Field):
- def get_manipulator_field_objs(self):
- return [formfields.LargeTextField]
-
-class TimeField(Field):
- empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
- self.auto_now, self.auto_now_add = auto_now, auto_now_add
- if auto_now or auto_now_add:
- kwargs['editable'] = False
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_db_prep_lookup(self, lookup_type, value):
- if lookup_type == 'range':
- value = [str(v) for v in value]
- else:
- value = str(value)
- return Field.get_db_prep_lookup(self, lookup_type, value)
-
- def pre_save(self, obj, value, add):
- if self.auto_now or (self.auto_now_add and add):
- setattr(obj, self.name, datetime.datetime.now().time())
-
- def get_db_prep_save(self, value, add):
- # Casts dates into string format for entry into database.
- if value is not None:
- value = value.strftime('%H:%M:%S')
- return Field.get_db_prep_save(self, value, add)
-
- def get_manipulator_field_objs(self):
- return [formfields.TimeField]
-
-class URLField(Field):
- def __init__(self, name, verbose_name=None, verify_exists=True, **kwargs):
- if verify_exists:
- kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.URLField]
-
-class USStateField(Field):
- def get_manipulator_field_objs(self):
- return [formfields.USStateField]
-
-class XMLField(Field):
- def __init__(self, name, verbose_name=None, schema_path=None, **kwargs):
- self.schema_path = schema_path
- Field.__init__(self, name, verbose_name, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
-
-class ForeignKey(Field):
- empty_strings_allowed = False
- def __init__(self, to, to_field=None, rel_name=None, **kwargs):
- try:
- to_name = to._meta.object_name.lower()
- except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
- assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
- kwargs['name'] = kwargs.get('name', '')
- kwargs['verbose_name'] = kwargs.get('verbose_name', '')
- else:
- to_field = to_field or to._meta.pk.name
- kwargs['name'] = kwargs.get('name', to_name + '_id')
- kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name)
- rel_name = rel_name or to_name
- kwargs['rel'] = ManyToOne(to, rel_name, to_field,
- num_in_admin=kwargs.pop('num_in_admin', 0),
- min_num_in_admin=kwargs.pop('min_num_in_admin', None),
- max_num_in_admin=kwargs.pop('max_num_in_admin', None),
- num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
- edit_inline=kwargs.pop('edit_inline', False),
- edit_inline_type=kwargs.pop('edit_inline_type', STACKED),
- related_name=kwargs.pop('related_name', None),
- limit_choices_to=kwargs.pop('limit_choices_to', None),
- lookup_overrides=kwargs.pop('lookup_overrides', None),
- raw_id_admin=kwargs.pop('raw_id_admin', False))
- Field.__init__(self, **kwargs)
-
- def get_manipulator_field_objs(self):
- return [formfields.IntegerField]
-
-class ManyToManyField(Field):
- def __init__(self, to, rel_name=None, **kwargs):
- kwargs['name'] = kwargs.get('name', to._meta.module_name)
- kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
- rel_name = rel_name or to._meta.object_name.lower()
- kwargs['rel'] = ManyToMany(to, rel_name,
- num_in_admin=kwargs.pop('num_in_admin', 0),
- related_name=kwargs.pop('related_name', None),
- filter_interface=kwargs.pop('filter_interface', None),
- limit_choices_to=kwargs.pop('limit_choices_to', None))
- Field.__init__(self, **kwargs)
-
- def get_manipulator_field_objs(self):
- choices = self.get_choices(include_blank=False)
- return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
-
- def get_m2m_db_table(self, original_opts):
- "Returns the name of the many-to-many 'join' table."
- return '%s_%s' % (original_opts.db_table, self.name)
-
-class OneToOneField(IntegerField):
- def __init__(self, to, to_field=None, rel_name=None, **kwargs):
- kwargs['name'] = kwargs.get('name', 'id')
- kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
- to_field = to_field or to._meta.pk.name
- rel_name = rel_name or to._meta.object_name.lower()
- kwargs['rel'] = OneToOne(to, rel_name, to_field,
- num_in_admin=kwargs.pop('num_in_admin', 0),
- edit_inline=kwargs.pop('edit_inline', False),
- edit_inline_type=kwargs.pop('edit_inline_type', STACKED),
- related_name=kwargs.pop('related_name', None),
- limit_choices_to=kwargs.pop('limit_choices_to', None),
- lookup_overrides=kwargs.pop('lookup_overrides', None),
- raw_id_admin=kwargs.pop('raw_id_admin', False))
- kwargs['primary_key'] = True
- IntegerField.__init__(self, **kwargs)
-
-####################
-# RELATIONSHIPS #
-####################
-
-class ManyToOne:
- def __init__(self, to, name, field_name, num_in_admin=0, min_num_in_admin=None,
- max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, edit_inline_type=STACKED,
- related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
- try:
- self.to = to._meta
- except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
- assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
- self.to = to
- self.name, self.field_name = name, field_name
- self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
- self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
- self.num_extra_on_change = num_extra_on_change
- self.edit_inline_type, self.related_name = edit_inline_type, related_name
- self.limit_choices_to = limit_choices_to or {}
- self.lookup_overrides = lookup_overrides or {}
- self.raw_id_admin = raw_id_admin
-
- def get_cache_name(self):
- return '_%s_cache' % self.name
-
- def get_related_field(self):
- "Returns the Field in the 'to' object to which this relationship is tied."
- return self.to.get_field(self.field_name)
-
-class ManyToMany:
- def __init__(self, to, name, num_in_admin=0, related_name=None,
- filter_interface=None, limit_choices_to=None):
- self.to, self.name = to._meta, name
- self.num_in_admin = num_in_admin
- self.related_name = related_name
- self.filter_interface = filter_interface
- self.limit_choices_to = limit_choices_to or {}
- self.edit_inline = False
-
-class OneToOne(ManyToOne):
- def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
- edit_inline_type=STACKED, related_name=None, limit_choices_to=None, lookup_overrides=None,
- raw_id_admin=False):
- self.to, self.name, self.field_name = to._meta, name, field_name
- self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
- self.edit_inline_type, self.related_name = edit_inline_type, related_name
- self.limit_choices_to = limit_choices_to or {}
- self.lookup_overrides = lookup_overrides or {}
- self.raw_id_admin = raw_id_admin
-
-class Admin:
- def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
- save_as=False, ordering=None, search_fields=None, save_on_top=False):
- self.fields = fields
- self.js = js or []
- self.list_display = list_display or ['__repr__']
- self.list_filter = list_filter or []
- self.date_hierarchy = date_hierarchy
- self.save_as, self.ordering = save_as, ordering
- self.search_fields = search_fields or []
- self.save_on_top = save_on_top
-
- def get_field_objs(self, opts):
- """
- Returns self.fields, except with fields as Field objects instead of
- field names. If self.fields is None, defaults to putting every
- non-AutoField field with editable=True in a single fieldset.
- """
- if self.fields is None:
- field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),)
- else:
- field_struct = self.fields
- new_fieldset_list = []
- for fieldset in field_struct:
- new_fieldset = [fieldset[0], {}]
- new_fieldset[1].update(fieldset[1])
- admin_fields = []
- for field_name_or_list in fieldset[1]['fields']:
- if isinstance(field_name_or_list, basestring):
- admin_fields.append([opts.get_field(field_name_or_list)])
- else:
- admin_fields.append([opts.get_field(field_name) for field_name in field_name_or_list])
- new_fieldset[1]['fields'] = admin_fields
- new_fieldset_list.append(new_fieldset)
- return new_fieldset_list
diff --git a/django/core/meta/fields.py b/django/core/meta/fields.py
new file mode 100644
index 0000000000..51769eaad9
--- /dev/null
+++ b/django/core/meta/fields.py
@@ -0,0 +1,667 @@
+from django.conf import settings
+from django.core import formfields, validators
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils.functional import curry
+from django.utils.text import capfirst
+import datetime, os
+
+# Random entropy string used by "default" param.
+NOT_PROVIDED = 'oijpwojefiojpanv'
+
+# Values for filter_interface.
+HORIZONTAL, VERTICAL = 1, 2
+
+# The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
+BLANK_CHOICE_DASH = [("", "---------")]
+BLANK_CHOICE_NONE = [("", "None")]
+
+# Values for Relation.edit_inline_type.
+TABULAR, STACKED = 1, 2
+
+RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
+
+# prepares a value for use in a LIKE query
+prep_for_like_query = lambda x: str(x).replace("%", "\%").replace("_", "\_")
+
+# returns the
class for a given radio_admin value
+get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
+
+def manipulator_valid_rel_key(f, self, field_data, all_data):
+ "Validates that the value is a valid foreign key"
+ mod = f.rel.to.get_model_module()
+ try:
+ mod.get_object(**{'id__iexact': field_data})
+ except ObjectDoesNotExist:
+ raise validators.ValidationError, "Please enter a valid %s." % f.verbose_name
+
+def manipulator_validator_unique(f, opts, self, field_data, all_data):
+ "Validates that the value is unique for this field."
+ try:
+ old_obj = opts.get_model_module().get_object(**{'%s__exact' % f.name: field_data})
+ except ObjectDoesNotExist:
+ return
+ if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
+ return
+ raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
+
+class Field(object):
+
+ # Designates whether empty strings fundamentally are allowed at the
+ # database level.
+ empty_strings_allowed = True
+
+ def __init__(self, name, verbose_name=None, primary_key=False,
+ maxlength=None, unique=False, blank=False, null=False, db_index=None,
+ core=False, rel=None, default=NOT_PROVIDED, editable=True,
+ prepopulate_from=None, unique_for_date=None, unique_for_month=None,
+ unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
+ help_text=''):
+ self.name = name
+ self.verbose_name = verbose_name or name.replace('_', ' ')
+ self.primary_key = primary_key
+ self.maxlength, self.unique = maxlength, unique
+ self.blank, self.null = blank, null
+ self.core, self.rel, self.default = core, rel, default
+ self.editable = editable
+ self.validator_list = validator_list or []
+ self.prepopulate_from = prepopulate_from
+ self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
+ self.unique_for_year = unique_for_year
+ self.choices = choices or []
+ self.radio_admin = radio_admin
+ self.help_text = help_text
+ if rel and isinstance(rel, ManyToMany):
+ self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
+
+ # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
+ if db_index is None:
+ if isinstance(rel, OneToOne) or isinstance(rel, ManyToOne):
+ self.db_index = True
+ else:
+ self.db_index = False
+ else:
+ self.db_index = db_index
+
+ def pre_save(self, obj, value, add):
+ """
+ Hook for altering the object obj based on the value of this field and
+ and on the add/change status.
+ """
+ pass
+
+ def get_db_prep_save(self, value, add):
+ "Returns field's value prepared for saving into a database."
+ return value
+
+ def get_db_prep_lookup(self, lookup_type, value):
+ "Returns field's value prepared for database lookup."
+ if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'month', 'day'):
+ return [value]
+ elif lookup_type in ('range', 'in'):
+ return value
+ elif lookup_type == 'year':
+ return ['%s-01-01' % value, '%s-12-31' % value]
+ elif lookup_type in ('contains', 'icontains'):
+ return ["%%%s%%" % prep_for_like_query(value)]
+ elif lookup_type == 'iexact':
+ return [prep_for_like_query(value)]
+ elif lookup_type in ('startswith', 'istartswith'):
+ return ["%s%%" % prep_for_like_query(value)]
+ elif lookup_type in ('endswith', 'iendswith'):
+ return ["%%%s" % prep_for_like_query(value)]
+ elif lookup_type == 'isnull':
+ return []
+ 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 != NOT_PROVIDED
+
+ def get_default(self):
+ "Returns the default value for this field."
+ if self.default != NOT_PROVIDED:
+ if hasattr(self.default, '__get_value__'):
+ return self.default.__get_value__()
+ return self.default
+ if self.null:
+ return None
+ return ""
+
+ def get_manipulator_field_names(self, name_prefix):
+ """
+ Returns a list of field names that this object adds to the manipulator.
+ """
+ return [name_prefix + self.name]
+
+ def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
+ """
+ Returns a list of formfields.FormField instances for this field. It
+ calculates the choices at runtime, not at compile time.
+
+ name_prefix is a prefix to prepend to the "field_name" argument.
+ rel is a boolean specifying whether this field is in a related context.
+ """
+ params = {'validator_list': self.validator_list[:]}
+ if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
+ params['maxlength'] = self.maxlength
+ if isinstance(self.rel, ManyToOne):
+ if self.rel.raw_id_admin:
+ field_objs = self.get_manipulator_field_objs()
+ params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
+ else:
+ if self.radio_admin:
+ field_objs = [formfields.RadioSelectField]
+ params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
+ params['ul_class'] = get_ul_class(self.radio_admin)
+ else:
+ if self.null:
+ field_objs = [formfields.NullSelectField]
+ else:
+ field_objs = [formfields.SelectField]
+ params['choices'] = self.get_choices()
+ elif self.choices:
+ if self.radio_admin:
+ field_objs = [formfields.RadioSelectField]
+ params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
+ params['ul_class'] = get_ul_class(self.radio_admin)
+ else:
+ field_objs = [formfields.SelectField]
+ params['choices'] = self.get_choices()
+ else:
+ field_objs = self.get_manipulator_field_objs()
+
+ # Add the "unique" validator(s).
+ for field_name_list in opts.unique_together:
+ if field_name_list[0] == self.name:
+ params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
+
+ # Add the "unique for..." validator(s).
+ if self.unique_for_date:
+ params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
+ if self.unique_for_month:
+ params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
+ if self.unique_for_year:
+ params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
+ if self.unique:
+ params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
+
+ # Only add is_required=True if the field cannot be blank. Primary keys
+ # are a special case, and fields in a related context should set this
+ # as False, because they'll be caught by a separate validator --
+ # RequiredIfOtherFieldGiven.
+ params['is_required'] = not self.blank and not self.primary_key and not rel
+
+ # If this field is in a related context, check whether any other fields
+ # in the related object have core=True. If so, add a validator --
+ # RequiredIfOtherFieldsGiven -- to this FormField.
+ if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
+ # First, get the core fields, if any.
+ core_field_names = []
+ for f in opts.fields:
+ if f.core and f != self:
+ core_field_names.extend(f.get_manipulator_field_names(name_prefix))
+ # Now, if there are any, add the validator to this FormField.
+ if core_field_names:
+ params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, "This field is required."))
+
+ # BooleanFields (CheckboxFields) are a special case. They don't take
+ # is_required or validator_list.
+ if isinstance(self, BooleanField):
+ del params['validator_list'], params['is_required']
+
+ # Finally, add the field_names.
+ field_names = self.get_manipulator_field_names(name_prefix)
+ return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
+
+ def get_manipulator_new_data(self, new_data, rel=False):
+ """
+ Given the full new_data dictionary (from the manipulator), returns this
+ field's data.
+ """
+ if rel:
+ return new_data.get(self.name, [self.get_default()])[0]
+ else:
+ val = new_data.get(self.name, self.get_default())
+ if not self.empty_strings_allowed and val == '' and self.null:
+ val = None
+ return val
+
+ def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
+ "Returns a list of tuples used as SelectField choices for this field."
+ first_choice = include_blank and blank_choice or []
+ if self.choices:
+ return first_choice + list(self.choices)
+ rel_obj = self.rel.to
+ return first_choice + [(getattr(x, rel_obj.pk.name), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
+
+class AutoField(Field):
+ empty_strings_allowed = False
+ def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
+ if not rel:
+ return [] # Don't add a FormField unless it's in a related context.
+ return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.HiddenField]
+
+ def get_manipulator_new_data(self, new_data, rel=False):
+ if not rel:
+ return None
+ return Field.get_manipulator_new_data(self, new_data, rel)
+
+class BooleanField(Field):
+ def __init__(self, *args, **kwargs):
+ kwargs['blank'] = True
+ Field.__init__(self, *args, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.CheckboxField]
+
+class CharField(Field):
+ def get_manipulator_field_objs(self):
+ return [formfields.TextField]
+
+class CommaSeparatedIntegerField(CharField):
+ def get_manipulator_field_objs(self):
+ return [formfields.CommaSeparatedIntegerField]
+
+class DateField(Field):
+ empty_strings_allowed = False
+ def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
+ self.auto_now, self.auto_now_add = auto_now, auto_now_add
+ if auto_now or auto_now_add:
+ kwargs['editable'] = False
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_db_prep_lookup(self, lookup_type, value):
+ if lookup_type == 'range':
+ value = [str(v) for v in value]
+ else:
+ value = str(value)
+ return Field.get_db_prep_lookup(self, lookup_type, value)
+
+ def pre_save(self, obj, value, add):
+ if self.auto_now or (self.auto_now_add and add):
+ setattr(obj, self.name, datetime.datetime.now())
+
+ def get_db_prep_save(self, value, add):
+ # Casts dates into string format for entry into database.
+ if value is not None:
+ value = value.strftime('%Y-%m-%d')
+ return Field.get_db_prep_save(self, value, add)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.DateField]
+
+class DateTimeField(DateField):
+ def get_db_prep_save(self, value, add):
+ # Casts dates into string format for entry into database.
+ if value is not None:
+ value = value.strftime('%Y-%m-%d %H:%M:%S')
+ return Field.get_db_prep_save(self, value, add)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.DateField, formfields.TimeField]
+
+ def get_manipulator_field_names(self, name_prefix):
+ return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
+
+ def get_manipulator_new_data(self, new_data, rel=False):
+ date_field, time_field = self.get_manipulator_field_names('')
+ if rel:
+ d = new_data.get(date_field, [None])[0]
+ t = new_data.get(time_field, [None])[0]
+ else:
+ d = new_data.get(date_field, None)
+ t = new_data.get(time_field, None)
+ if d is not None and t is not None:
+ return datetime.datetime.combine(d, t)
+ return self.get_default()
+
+class EmailField(Field):
+ def get_manipulator_field_objs(self):
+ return [formfields.EmailField]
+
+class FileField(Field):
+ def __init__(self, name, verbose_name=None, upload_to='', **kwargs):
+ self.upload_to = upload_to
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
+ field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
+
+ if not self.blank:
+ if rel:
+ # This validator makes sure FileFields work in a related context.
+ class RequiredFileField:
+ def __init__(self, other_field_names, other_file_field_name):
+ self.other_field_names = other_field_names
+ self.other_file_field_name = other_file_field_name
+ self.always_test = True
+ def __call__(self, field_data, all_data):
+ if not all_data.get(self.other_file_field_name, False):
+ c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, "This field is required.")
+ c(field_data, all_data)
+ # First, get the core fields, if any.
+ core_field_names = []
+ for f in opts.fields:
+ if f.core and f != self:
+ core_field_names.extend(f.get_manipulator_field_names(name_prefix))
+ # Now, if there are any, add the validator to this FormField.
+ if core_field_names:
+ field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
+ else:
+ v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, "This field is required.")
+ v.always_test = True
+ field_list[0].validator_list.append(v)
+ field_list[0].is_required = field_list[1].is_required = False
+
+ # If the raw path is passed in, validate it's under the MEDIA_ROOT.
+ def isWithinMediaRoot(field_data, all_data):
+ f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
+ if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
+ raise validators.ValidationError, "Enter a valid filename."
+ field_list[1].validator_list.append(isWithinMediaRoot)
+ return field_list
+
+ def get_manipulator_field_objs(self):
+ return [formfields.FileUploadField, formfields.HiddenField]
+
+ def get_manipulator_field_names(self, name_prefix):
+ return [name_prefix + self.name + '_file', name_prefix + self.name]
+
+ def save_file(self, new_data, new_object, original_object, change, rel):
+ upload_field_name = self.get_manipulator_field_names('')[0]
+ if new_data.get(upload_field_name, False):
+ if rel:
+ getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
+ else:
+ getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
+
+ def get_directory_name(self):
+ return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
+
+ def get_filename(self, filename):
+ from django.utils.text import get_valid_filename
+ f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
+ return os.path.normpath(f)
+
+class FloatField(Field):
+ empty_strings_allowed = False
+ def __init__(self, name, verbose_name=None, max_digits=None, decimal_places=None, **kwargs):
+ self.max_digits, self.decimal_places = max_digits, decimal_places
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
+
+class ImageField(FileField):
+ def __init__(self, name, verbose_name=None, width_field=None, height_field=None, **kwargs):
+ self.width_field, self.height_field = width_field, height_field
+ FileField.__init__(self, name, verbose_name, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.ImageUploadField, formfields.HiddenField]
+
+ def save_file(self, new_data, new_object, original_object, change, rel):
+ FileField.save_file(self, new_data, new_object, original_object, change, rel)
+ # If the image has height and/or width field(s) and they haven't
+ # changed, set the width and/or height field(s) back to their original
+ # values.
+ if change and (self.width_field or self.height_field):
+ if self.width_field:
+ setattr(new_object, self.width_field, getattr(original_object, self.width_field))
+ if self.height_field:
+ setattr(new_object, self.height_field, getattr(original_object, self.height_field))
+ new_object.save()
+
+class IntegerField(Field):
+ empty_strings_allowed = False
+ def get_manipulator_field_objs(self):
+ return [formfields.IntegerField]
+
+class IPAddressField(Field):
+ def __init__(self, *args, **kwargs):
+ kwargs['maxlength'] = 15
+ Field.__init__(self, *args, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.IPAddressField]
+
+class NullBooleanField(Field):
+ def __init__(self, *args, **kwargs):
+ kwargs['null'] = True
+ Field.__init__(self, *args, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.NullBooleanField]
+
+class PhoneNumberField(IntegerField):
+ def get_manipulator_field_objs(self):
+ return [formfields.PhoneNumberField]
+
+class PositiveIntegerField(IntegerField):
+ def get_manipulator_field_objs(self):
+ return [formfields.PositiveIntegerField]
+
+class PositiveSmallIntegerField(IntegerField):
+ def get_manipulator_field_objs(self):
+ return [formfields.PositiveSmallIntegerField]
+
+class SlugField(Field):
+ def __init__(self, *args, **kwargs):
+ kwargs['maxlength'] = 50
+ kwargs.setdefault('validator_list', []).append(validators.isAlphaNumeric)
+ # Set db_index=True unless it's been set manually.
+ if not kwargs.has_key('db_index'):
+ kwargs['db_index'] = True
+ Field.__init__(self, *args, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.TextField]
+
+class SmallIntegerField(IntegerField):
+ def get_manipulator_field_objs(self):
+ return [formfields.SmallIntegerField]
+
+class TextField(Field):
+ def get_manipulator_field_objs(self):
+ return [formfields.LargeTextField]
+
+class TimeField(Field):
+ empty_strings_allowed = False
+ def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
+ self.auto_now, self.auto_now_add = auto_now, auto_now_add
+ if auto_now or auto_now_add:
+ kwargs['editable'] = False
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_db_prep_lookup(self, lookup_type, value):
+ if lookup_type == 'range':
+ value = [str(v) for v in value]
+ else:
+ value = str(value)
+ return Field.get_db_prep_lookup(self, lookup_type, value)
+
+ def pre_save(self, obj, value, add):
+ if self.auto_now or (self.auto_now_add and add):
+ setattr(obj, self.name, datetime.datetime.now().time())
+
+ def get_db_prep_save(self, value, add):
+ # Casts dates into string format for entry into database.
+ if value is not None:
+ value = value.strftime('%H:%M:%S')
+ return Field.get_db_prep_save(self, value, add)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.TimeField]
+
+class URLField(Field):
+ def __init__(self, name, verbose_name=None, verify_exists=True, **kwargs):
+ if verify_exists:
+ kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.URLField]
+
+class USStateField(Field):
+ def get_manipulator_field_objs(self):
+ return [formfields.USStateField]
+
+class XMLField(Field):
+ def __init__(self, name, verbose_name=None, schema_path=None, **kwargs):
+ self.schema_path = schema_path
+ Field.__init__(self, name, verbose_name, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
+
+class ForeignKey(Field):
+ empty_strings_allowed = False
+ def __init__(self, to, to_field=None, rel_name=None, **kwargs):
+ try:
+ to_name = to._meta.object_name.lower()
+ except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
+ assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
+ kwargs['name'] = kwargs.get('name', '')
+ kwargs['verbose_name'] = kwargs.get('verbose_name', '')
+ else:
+ to_field = to_field or to._meta.pk.name
+ kwargs['name'] = kwargs.get('name', to_name + '_id')
+ kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name)
+ rel_name = rel_name or to_name
+ kwargs['rel'] = ManyToOne(to, rel_name, to_field,
+ num_in_admin=kwargs.pop('num_in_admin', 0),
+ min_num_in_admin=kwargs.pop('min_num_in_admin', None),
+ max_num_in_admin=kwargs.pop('max_num_in_admin', None),
+ num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
+ edit_inline=kwargs.pop('edit_inline', False),
+ edit_inline_type=kwargs.pop('edit_inline_type', STACKED),
+ related_name=kwargs.pop('related_name', None),
+ limit_choices_to=kwargs.pop('limit_choices_to', None),
+ lookup_overrides=kwargs.pop('lookup_overrides', None),
+ raw_id_admin=kwargs.pop('raw_id_admin', False))
+ Field.__init__(self, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ return [formfields.IntegerField]
+
+class ManyToManyField(Field):
+ def __init__(self, to, rel_name=None, **kwargs):
+ kwargs['name'] = kwargs.get('name', to._meta.module_name)
+ kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
+ rel_name = rel_name or to._meta.object_name.lower()
+ kwargs['rel'] = ManyToMany(to, rel_name,
+ num_in_admin=kwargs.pop('num_in_admin', 0),
+ related_name=kwargs.pop('related_name', None),
+ filter_interface=kwargs.pop('filter_interface', None),
+ limit_choices_to=kwargs.pop('limit_choices_to', None))
+ Field.__init__(self, **kwargs)
+
+ def get_manipulator_field_objs(self):
+ choices = self.get_choices(include_blank=False)
+ return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
+
+ def get_m2m_db_table(self, original_opts):
+ "Returns the name of the many-to-many 'join' table."
+ return '%s_%s' % (original_opts.db_table, self.name)
+
+class OneToOneField(IntegerField):
+ def __init__(self, to, to_field=None, rel_name=None, **kwargs):
+ kwargs['name'] = kwargs.get('name', 'id')
+ kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
+ to_field = to_field or to._meta.pk.name
+ rel_name = rel_name or to._meta.object_name.lower()
+ kwargs['rel'] = OneToOne(to, rel_name, to_field,
+ num_in_admin=kwargs.pop('num_in_admin', 0),
+ edit_inline=kwargs.pop('edit_inline', False),
+ edit_inline_type=kwargs.pop('edit_inline_type', STACKED),
+ related_name=kwargs.pop('related_name', None),
+ limit_choices_to=kwargs.pop('limit_choices_to', None),
+ lookup_overrides=kwargs.pop('lookup_overrides', None),
+ raw_id_admin=kwargs.pop('raw_id_admin', False))
+ kwargs['primary_key'] = True
+ IntegerField.__init__(self, **kwargs)
+
+class ManyToOne:
+ def __init__(self, to, name, field_name, num_in_admin=0, min_num_in_admin=None,
+ max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, edit_inline_type=STACKED,
+ related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
+ try:
+ self.to = to._meta
+ except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
+ assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
+ self.to = to
+ self.name, self.field_name = name, field_name
+ self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
+ self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
+ self.num_extra_on_change = num_extra_on_change
+ self.edit_inline_type, self.related_name = edit_inline_type, related_name
+ self.limit_choices_to = limit_choices_to or {}
+ self.lookup_overrides = lookup_overrides or {}
+ self.raw_id_admin = raw_id_admin
+
+ def get_cache_name(self):
+ return '_%s_cache' % self.name
+
+ def get_related_field(self):
+ "Returns the Field in the 'to' object to which this relationship is tied."
+ return self.to.get_field(self.field_name)
+
+class ManyToMany:
+ def __init__(self, to, name, num_in_admin=0, related_name=None,
+ filter_interface=None, limit_choices_to=None):
+ self.to, self.name = to._meta, name
+ self.num_in_admin = num_in_admin
+ self.related_name = related_name
+ self.filter_interface = filter_interface
+ self.limit_choices_to = limit_choices_to or {}
+ self.edit_inline = False
+
+class OneToOne(ManyToOne):
+ def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
+ edit_inline_type=STACKED, related_name=None, limit_choices_to=None, lookup_overrides=None,
+ raw_id_admin=False):
+ self.to, self.name, self.field_name = to._meta, name, field_name
+ self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
+ self.edit_inline_type, self.related_name = edit_inline_type, related_name
+ self.limit_choices_to = limit_choices_to or {}
+ self.lookup_overrides = lookup_overrides or {}
+ self.raw_id_admin = raw_id_admin
+
+class Admin:
+ def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
+ save_as=False, ordering=None, search_fields=None, save_on_top=False):
+ self.fields = fields
+ self.js = js or []
+ self.list_display = list_display or ['__repr__']
+ self.list_filter = list_filter or []
+ self.date_hierarchy = date_hierarchy
+ self.save_as, self.ordering = save_as, ordering
+ self.search_fields = search_fields or []
+ self.save_on_top = save_on_top
+
+ def get_field_objs(self, opts):
+ """
+ Returns self.fields, except with fields as Field objects instead of
+ field names. If self.fields is None, defaults to putting every
+ non-AutoField field with editable=True in a single fieldset.
+ """
+ if self.fields is None:
+ field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),)
+ else:
+ field_struct = self.fields
+ new_fieldset_list = []
+ for fieldset in field_struct:
+ new_fieldset = [fieldset[0], {}]
+ new_fieldset[1].update(fieldset[1])
+ admin_fields = []
+ for field_name_or_list in fieldset[1]['fields']:
+ if isinstance(field_name_or_list, basestring):
+ admin_fields.append([opts.get_field(field_name_or_list)])
+ else:
+ admin_fields.append([opts.get_field(field_name) for field_name in field_name_or_list])
+ new_fieldset[1]['fields'] = admin_fields
+ new_fieldset_list.append(new_fieldset)
+ return new_fieldset_list
diff --git a/django/models/__init__.py b/django/models/__init__.py
index b4a1d75617..32350200a0 100644
--- a/django/models/__init__.py
+++ b/django/models/__init__.py
@@ -1,4 +1,5 @@
from django.core import meta
+from django.utils.functional import curry
__all__ = ['auth', 'core']
@@ -28,30 +29,30 @@ for mod in modules:
if isinstance(rel_field.rel, meta.OneToOne):
# Add "get_thingie" methods for one-to-one related objects.
# EXAMPLE: Place.get_restaurants_restaurant()
- func = meta.curry(meta.method_get_related, 'get_object', rel_mod, rel_field)
+ func = curry(meta.method_get_related, 'get_object', rel_mod, rel_field)
func.__doc__ = "Returns the associated `%s.%s` object." % (rel_obj.app_label, rel_obj.module_name)
setattr(klass, 'get_%s' % rel_obj_name, func)
elif isinstance(rel_field.rel, meta.ManyToOne):
# Add "get_thingie" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice()
- func = meta.curry(meta.method_get_related, 'get_object', rel_mod, rel_field)
+ func = curry(meta.method_get_related, 'get_object', rel_mod, rel_field)
func.__doc__ = "Returns the associated `%s.%s` object matching the given criteria." % (rel_obj.app_label, rel_obj.module_name)
setattr(klass, 'get_%s' % rel_obj_name, func)
# Add "get_thingie_count" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_count()
- func = meta.curry(meta.method_get_related, 'get_count', rel_mod, rel_field)
+ func = curry(meta.method_get_related, 'get_count', rel_mod, rel_field)
func.__doc__ = "Returns the number of associated `%s.%s` objects." % (rel_obj.app_label, rel_obj.module_name)
setattr(klass, 'get_%s_count' % rel_obj_name, func)
# Add "get_thingie_list" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_list()
- func = meta.curry(meta.method_get_related, 'get_list', rel_mod, rel_field)
+ func = curry(meta.method_get_related, 'get_list', rel_mod, rel_field)
func.__doc__ = "Returns a list of associated `%s.%s` objects." % (rel_obj.app_label, rel_obj.module_name)
setattr(klass, 'get_%s_list' % rel_obj_name, func)
# Add "add_thingie" methods for many-to-one related objects,
# but only for related objects that are in the same app.
# EXAMPLE: Poll.add_choice()
if rel_obj.app_label == klass._meta.app_label:
- func = meta.curry(meta.method_add_related, rel_obj, rel_mod, rel_field)
+ func = curry(meta.method_add_related, rel_obj, rel_mod, rel_field)
func.alters_data = True
setattr(klass, 'add_%s' % rel_obj_name, func)
del func
@@ -61,11 +62,11 @@ for mod in modules:
for rel_opts, rel_field in klass._meta.get_all_related_many_to_many_objects():
rel_mod = rel_opts.get_model_module()
rel_obj_name = klass._meta.get_rel_object_method_name(rel_opts, rel_field)
- setattr(klass, 'get_%s' % rel_obj_name, meta.curry(meta.method_get_related_many_to_many, 'get_object', rel_mod, rel_field))
- setattr(klass, 'get_%s_count' % rel_obj_name, meta.curry(meta.method_get_related_many_to_many, 'get_count', rel_mod, rel_field))
- setattr(klass, 'get_%s_list' % rel_obj_name, meta.curry(meta.method_get_related_many_to_many, 'get_list', rel_mod, rel_field))
+ setattr(klass, 'get_%s' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_object', rel_mod, rel_field))
+ setattr(klass, 'get_%s_count' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_count', rel_mod, rel_field))
+ setattr(klass, 'get_%s_list' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_list', rel_mod, rel_field))
if rel_opts.app_label == klass._meta.app_label:
- func = meta.curry(meta.method_set_related_many_to_many, rel_opts, rel_field)
+ func = curry(meta.method_set_related_many_to_many, rel_opts, rel_field)
func.alters_data = True
setattr(klass, 'set_%s' % rel_opts.module_name, func)
del func
@@ -74,12 +75,12 @@ for mod in modules:
# Add "set_thingie_order" and "get_thingie_order" methods for objects
# that are ordered with respect to this.
for obj in klass._meta.get_ordered_objects():
- func = meta.curry(meta.method_set_order, obj)
+ func = curry(meta.method_set_order, obj)
func.__doc__ = "Sets the order of associated `%s.%s` objects to the given ID list." % (obj.app_label, obj.module_name)
func.alters_data = True
setattr(klass, 'set_%s_order' % obj.object_name.lower(), func)
- func = meta.curry(meta.method_get_order, obj)
+ func = curry(meta.method_get_order, obj)
func.__doc__ = "Returns the order of associated `%s.%s` objects as a list of IDs." % (obj.app_label, obj.module_name)
setattr(klass, 'get_%s_order' % obj.object_name.lower(), func)
del func, obj # clean up
diff --git a/django/templatetags/adminapplist.py b/django/templatetags/adminapplist.py
index 069fb4529b..1f5e3db125 100644
--- a/django/templatetags/adminapplist.py
+++ b/django/templatetags/adminapplist.py
@@ -6,10 +6,11 @@ class AdminApplistNode(template.Node):
def render(self, context):
from django.core import meta
+ from django.utils.text import capfirst
app_list = []
for app in meta.get_installed_model_modules():
app_label = app.__name__[app.__name__.rindex('.')+1:]
- model_list = [{'name': meta.capfirst(m._meta.verbose_name_plural),
+ model_list = [{'name': capfirst(m._meta.verbose_name_plural),
'admin_url': '%s/%s/' % (app_label, m._meta.module_name)} \
for m in app._MODELS if m._meta.admin]
if model_list:
diff --git a/django/utils/functional.py b/django/utils/functional.py
new file mode 100644
index 0000000000..f3b0598763
--- /dev/null
+++ b/django/utils/functional.py
@@ -0,0 +1,4 @@
+def curry(*args, **kwargs):
+ def _curried(*moreargs, **morekwargs):
+ return args[0](*(args[1:]+moreargs), **dict(kwargs.items() + morekwargs.items()))
+ return _curried
diff --git a/django/utils/text.py b/django/utils/text.py
index a0d106bb31..d8b2e16e82 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -1,5 +1,8 @@
import re
+# Capitalizes the first letter of a string.
+capfirst = lambda x: x and x[0].upper() + x[1:]
+
def wrap(text, width):
"""
A word-wrap function that preserves existing line breaks and most spaces in
diff --git a/django/views/admin/main.py b/django/views/admin/main.py
index 4b812a802c..9b0c459418 100644
--- a/django/views/admin/main.py
+++ b/django/views/admin/main.py
@@ -6,7 +6,7 @@ from django.core.extensions import DjangoContext as Context
from django.models.auth import log
from django.utils.html import strip_tags
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
-from django.utils.text import get_text_list
+from django.utils.text import capfirst, get_text_list
from django.conf.settings import ADMIN_MEDIA_PREFIX
import operator
@@ -266,7 +266,7 @@ def change_list(request, app_label, module_name):
raw_template = ['{% extends "base_site" %}\n']
raw_template.append('{% block bodyclass %}change-list{% endblock %}\n')
if not is_popup:
- raw_template.append('{%% block breadcrumbs %%}
\n')
if change:
t.append('{% if not is_popup %}')
@@ -613,12 +613,12 @@ def _get_template(opts, app_label, add=False, change=False, show_delete=False, f
if change and hasattr(rel_obj, 'get_absolute_url'):
view_on_site = '{%% if %s.original %%}View on site{%% endif %%}' % (var_name, var_name, var_name)
if rel_field.rel.edit_inline_type == meta.TABULAR:
- t.append('