Refactored meta.py -- created a django.core.meta package, with init.py and fields.py

git-svn-id: http://code.djangoproject.com/svn/django/trunk@378 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2005-08-01 21:29:52 +00:00
parent 006e9e789b
commit e0c3dd3318
8 changed files with 712 additions and 710 deletions

View File

@ -264,7 +264,7 @@ database_check.args = APP_ARGS
def get_admin_index(mod): def get_admin_index(mod):
"Returns admin-index template snippet (in list form) for the given module." "Returns admin-index template snippet (in list form) for the given module."
from django.core import meta from django.utils.text import capfirst
output = [] output = []
app_label = mod._MODELS[0]._meta.app_label app_label = mod._MODELS[0]._meta.app_label
output.append('{%% if perms.%s %%}' % app_label) output.append('{%% if perms.%s %%}' % app_label)
@ -274,7 +274,7 @@ def get_admin_index(mod):
output.append(MODULE_TEMPLATE % { output.append(MODULE_TEMPLATE % {
'app': app_label, 'app': app_label,
'mod': klass._meta.module_name, '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(), 'addperm': klass._meta.get_add_permission(),
'changeperm': klass._meta.get_change_permission(), 'changeperm': klass._meta.get_change_permission(),
}) })

View File

@ -1,30 +1,20 @@
from django.conf import settings
from django.core import formfields, validators from django.core import formfields, validators
from django.core import db from django.core import db
from django.core.exceptions import ObjectDoesNotExist 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 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. # Admin stages.
ADD, CHANGE, BOTH = 1, 2, 3 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. # Size of each "chunk" for get_iterator calls.
# Larger values are slightly faster at the expense of more storage space. # Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100 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' MODEL_PREFIX = 'django.models'
# Methods on models with the following prefix will be removed and # Methods on models with the following prefix will be removed and
@ -37,21 +27,10 @@ MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_'
LOOKUP_SEPARATOR = '__' LOOKUP_SEPARATOR = '__'
RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
#################### ####################
# HELPER FUNCTIONS # # 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 <ul> 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. # Django currently supports two forms of ordering.
# Form 1 (deprecated) example: # Form 1 (deprecated) example:
# order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM')) # 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)) output.append('%s%s ASC' % (prefix, f))
return ', '.join(output) 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): def get_module(app_label, module_name):
return __import__('%s.%s.%s' % (MODEL_PREFIX, 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) getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
return new_object 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): def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.utils.text import get_text_list from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_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' 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." % \ 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)) (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

667
django/core/meta/fields.py Normal file
View File

@ -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 <ul> 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

View File

@ -1,4 +1,5 @@
from django.core import meta from django.core import meta
from django.utils.functional import curry
__all__ = ['auth', 'core'] __all__ = ['auth', 'core']
@ -28,30 +29,30 @@ for mod in modules:
if isinstance(rel_field.rel, meta.OneToOne): if isinstance(rel_field.rel, meta.OneToOne):
# Add "get_thingie" methods for one-to-one related objects. # Add "get_thingie" methods for one-to-one related objects.
# EXAMPLE: Place.get_restaurants_restaurant() # 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) func.__doc__ = "Returns the associated `%s.%s` object." % (rel_obj.app_label, rel_obj.module_name)
setattr(klass, 'get_%s' % rel_obj_name, func) setattr(klass, 'get_%s' % rel_obj_name, func)
elif isinstance(rel_field.rel, meta.ManyToOne): elif isinstance(rel_field.rel, meta.ManyToOne):
# Add "get_thingie" methods for many-to-one related objects. # Add "get_thingie" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice() # 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) 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) setattr(klass, 'get_%s' % rel_obj_name, func)
# Add "get_thingie_count" methods for many-to-one related objects. # Add "get_thingie_count" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_count() # 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) 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) setattr(klass, 'get_%s_count' % rel_obj_name, func)
# Add "get_thingie_list" methods for many-to-one related objects. # Add "get_thingie_list" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_list() # 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) 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) setattr(klass, 'get_%s_list' % rel_obj_name, func)
# Add "add_thingie" methods for many-to-one related objects, # Add "add_thingie" methods for many-to-one related objects,
# but only for related objects that are in the same app. # but only for related objects that are in the same app.
# EXAMPLE: Poll.add_choice() # EXAMPLE: Poll.add_choice()
if rel_obj.app_label == klass._meta.app_label: 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 func.alters_data = True
setattr(klass, 'add_%s' % rel_obj_name, func) setattr(klass, 'add_%s' % rel_obj_name, func)
del 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(): for rel_opts, rel_field in klass._meta.get_all_related_many_to_many_objects():
rel_mod = rel_opts.get_model_module() rel_mod = rel_opts.get_model_module()
rel_obj_name = klass._meta.get_rel_object_method_name(rel_opts, rel_field) 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' % 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, meta.curry(meta.method_get_related_many_to_many, 'get_count', 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, meta.curry(meta.method_get_related_many_to_many, 'get_list', 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: 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 func.alters_data = True
setattr(klass, 'set_%s' % rel_opts.module_name, func) setattr(klass, 'set_%s' % rel_opts.module_name, func)
del func del func
@ -74,12 +75,12 @@ for mod in modules:
# Add "set_thingie_order" and "get_thingie_order" methods for objects # Add "set_thingie_order" and "get_thingie_order" methods for objects
# that are ordered with respect to this. # that are ordered with respect to this.
for obj in klass._meta.get_ordered_objects(): 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.__doc__ = "Sets the order of associated `%s.%s` objects to the given ID list." % (obj.app_label, obj.module_name)
func.alters_data = True func.alters_data = True
setattr(klass, 'set_%s_order' % obj.object_name.lower(), func) 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) 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) setattr(klass, 'get_%s_order' % obj.object_name.lower(), func)
del func, obj # clean up del func, obj # clean up

View File

@ -6,10 +6,11 @@ class AdminApplistNode(template.Node):
def render(self, context): def render(self, context):
from django.core import meta from django.core import meta
from django.utils.text import capfirst
app_list = [] app_list = []
for app in meta.get_installed_model_modules(): for app in meta.get_installed_model_modules():
app_label = app.__name__[app.__name__.rindex('.')+1:] 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)} \ 'admin_url': '%s/%s/' % (app_label, m._meta.module_name)} \
for m in app._MODELS if m._meta.admin] for m in app._MODELS if m._meta.admin]
if model_list: if model_list:

View File

@ -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

View File

@ -1,5 +1,8 @@
import re import re
# Capitalizes the first letter of a string.
capfirst = lambda x: x and x[0].upper() + x[1:]
def wrap(text, width): def wrap(text, width):
""" """
A word-wrap function that preserves existing line breaks and most spaces in A word-wrap function that preserves existing line breaks and most spaces in

View File

@ -6,7 +6,7 @@ from django.core.extensions import DjangoContext as Context
from django.models.auth import log from django.models.auth import log
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect 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 from django.conf.settings import ADMIN_MEDIA_PREFIX
import operator import operator
@ -266,7 +266,7 @@ def change_list(request, app_label, module_name):
raw_template = ['{% extends "base_site" %}\n'] raw_template = ['{% extends "base_site" %}\n']
raw_template.append('{% block bodyclass %}change-list{% endblock %}\n') raw_template.append('{% block bodyclass %}change-list{% endblock %}\n')
if not is_popup: if not is_popup:
raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; %s</div>{%% endblock %%}\n' % meta.capfirst(opts.verbose_name_plural)) raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; %s</div>{%% endblock %%}\n' % capfirst(opts.verbose_name_plural))
raw_template.append('{% block coltype %}flex{% endblock %}') raw_template.append('{% block coltype %}flex{% endblock %}')
raw_template.append('{% block content %}\n') raw_template.append('{% block content %}\n')
raw_template.append('<div id="content-main">\n') raw_template.append('<div id="content-main">\n')
@ -356,10 +356,10 @@ def change_list(request, app_label, module_name):
except AttributeError: except AttributeError:
header = func.__name__ header = func.__name__
# Non-field list_display values don't get ordering capability. # Non-field list_display values don't get ordering capability.
raw_template.append('<th>%s</th>' % meta.capfirst(header)) raw_template.append('<th>%s</th>' % capfirst(header))
else: else:
if isinstance(f.rel, meta.ManyToOne) and f.null: if isinstance(f.rel, meta.ManyToOne) and f.null:
raw_template.append('<th>%s</th>' % meta.capfirst(f.verbose_name)) raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
else: else:
th_classes = [] th_classes = []
new_order_type = 'asc' new_order_type = 'asc'
@ -369,7 +369,7 @@ def change_list(request, app_label, module_name):
raw_template.append('<th%s><a href="%s">%s</a></th>' % \ raw_template.append('<th%s><a href="%s">%s</a></th>' % \
((th_classes and ' class="%s"' % ' '.join(th_classes) or ''), ((th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
meta.capfirst(f.verbose_name))) capfirst(f.verbose_name)))
raw_template.append('</tr>\n</thead>\n') raw_template.append('</tr>\n</thead>\n')
# Result rows. # Result rows.
pk = lookup_opts.pk.name pk = lookup_opts.pk.name
@ -572,7 +572,7 @@ def _get_template(opts, app_label, add=False, change=False, show_delete=False, f
t.append('{%% block bodyclass %%}%s-%s change-form{%% endblock %%}\n' % (app_label, opts.object_name.lower())) t.append('{%% block bodyclass %%}%s-%s change-form{%% endblock %%}\n' % (app_label, opts.object_name.lower()))
breadcrumb_title = add and "Add %s" % opts.verbose_name or '{{ original|striptags|truncatewords:"18" }}' breadcrumb_title = add and "Add %s" % opts.verbose_name or '{{ original|striptags|truncatewords:"18" }}'
t.append('{%% block breadcrumbs %%}{%% if not is_popup %%}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../">%s</a> &rsaquo; %s</div>{%% endif %%}{%% endblock %%}\n' % \ t.append('{%% block breadcrumbs %%}{%% if not is_popup %%}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../">%s</a> &rsaquo; %s</div>{%% endif %%}{%% endblock %%}\n' % \
(meta.capfirst(opts.verbose_name_plural), breadcrumb_title)) (capfirst(opts.verbose_name_plural), breadcrumb_title))
t.append('{% block content %}<div id="content-main">\n') t.append('{% block content %}<div id="content-main">\n')
if change: if change:
t.append('{% if not is_popup %}') 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'): if change and hasattr(rel_obj, 'get_absolute_url'):
view_on_site = '{%% if %s.original %%}<a href="/r/{{ %s.content_type_id }}/{{ %s.original.id }}/">View on site</a>{%% endif %%}' % (var_name, var_name, var_name) view_on_site = '{%% if %s.original %%}<a href="/r/{{ %s.content_type_id }}/{{ %s.original.id }}/">View on site</a>{%% endif %%}' % (var_name, var_name, var_name)
if rel_field.rel.edit_inline_type == meta.TABULAR: if rel_field.rel.edit_inline_type == meta.TABULAR:
t.append('<h2>%s</h2>\n<table>\n' % meta.capfirst(rel_obj.verbose_name_plural)) t.append('<h2>%s</h2>\n<table>\n' % capfirst(rel_obj.verbose_name_plural))
t.append('<thead><tr>') t.append('<thead><tr>')
for f in field_list: for f in field_list:
if isinstance(f, meta.AutoField): if isinstance(f, meta.AutoField):
continue continue
t.append('<th%s>%s</th>' % (f.blank and ' class="optional"' or '', meta.capfirst(f.verbose_name))) t.append('<th%s>%s</th>' % (f.blank and ' class="optional"' or '', capfirst(f.verbose_name)))
t.append('</tr></thead>\n') t.append('</tr></thead>\n')
t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name))
if change: if change:
@ -656,7 +656,7 @@ def _get_template(opts, app_label, add=False, change=False, show_delete=False, f
t.append('{% endfor %}\n') t.append('{% endfor %}\n')
else: # edit_inline_type == STACKED else: # edit_inline_type == STACKED
t.append('{%% for %s in form.%s %%}' % (var_name, rel_obj.module_name)) t.append('{%% for %s in form.%s %%}' % (var_name, rel_obj.module_name))
t.append('<h2>%s #{{ forloop.counter }}</h2>' % meta.capfirst(rel_obj.verbose_name)) t.append('<h2>%s #{{ forloop.counter }}</h2>' % capfirst(rel_obj.verbose_name))
if view_on_site: if view_on_site:
t.append('<p>%s</p>' % view_on_site) t.append('<p>%s</p>' % view_on_site)
for f in field_list: for f in field_list:
@ -710,14 +710,14 @@ def _get_admin_field(field_list, name_prefix, rel, add, change):
# the *left* of the label. # the *left* of the label.
if isinstance(field, meta.BooleanField): if isinstance(field, meta.BooleanField):
t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change))
t.append(' <label for="%s" class="vCheckboxLabel">%s</label>' % (label_name, meta.capfirst(field.verbose_name))) t.append(' <label for="%s" class="vCheckboxLabel">%s</label>' % (label_name, capfirst(field.verbose_name)))
else: else:
class_names = [] class_names = []
if not field.blank: if not field.blank:
class_names.append('required') class_names.append('required')
if i > 0: if i > 0:
class_names.append('inline') class_names.append('inline')
t.append('<label for="%s"%s>%s:</label> ' % (label_name, class_names and ' class="%s"' % ' '.join(class_names) or '', meta.capfirst(field.verbose_name))) t.append('<label for="%s"%s>%s:</label> ' % (label_name, class_names and ' class="%s"' % ' '.join(class_names) or '', capfirst(field.verbose_name)))
t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change))
if change and use_raw_id_admin(field): if change and use_raw_id_admin(field):
obj_repr = '%soriginal.get_%s|truncatewords:"14"' % (rel and name_prefix or '', field.rel.name) obj_repr = '%soriginal.get_%s|truncatewords:"14"' % (rel and name_prefix or '', field.rel.name)
@ -991,11 +991,11 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if rel_field.rel.edit_inline or not rel_opts.admin: if rel_field.rel.edit_inline or not rel_opts.admin:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
nh(deleted_objects, current_depth, ['%s: %r' % (meta.capfirst(rel_opts.verbose_name), sub_obj), []]) nh(deleted_objects, current_depth, ['%s: %r' % (capfirst(rel_opts.verbose_name), sub_obj), []])
else: else:
# Display a link to the admin page. # Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%r</a>' % \ nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%r</a>' % \
(meta.capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name,
getattr(sub_obj, rel_opts.pk.name), sub_obj), []]) getattr(sub_obj, rel_opts.pk.name), sub_obj), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2)
else: else:
@ -1005,11 +1005,11 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if rel_field.rel.edit_inline or not rel_opts.admin: if rel_field.rel.edit_inline or not rel_opts.admin:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
nh(deleted_objects, current_depth, ['%s: %s' % (meta.capfirst(rel_opts.verbose_name), strip_tags(repr(sub_obj))), []]) nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(rel_opts.verbose_name), strip_tags(repr(sub_obj))), []])
else: else:
# Display a link to the admin page. # Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(meta.capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2)
# If there were related objects, and the user doesn't have # If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed. # permission to delete them, add the missing perm to perms_needed.
@ -1053,7 +1053,7 @@ def delete_stage(request, app_label, module_name, object_id):
# Populate deleted_objects, a data structure of all related objects that # Populate deleted_objects, a data structure of all related objects that
# will also be deleted. # will also be deleted.
deleted_objects = ['%s: <a href="../../%s/">%s</a>' % (meta.capfirst(opts.verbose_name), object_id, strip_tags(repr(obj))), []] deleted_objects = ['%s: <a href="../../%s/">%s</a>' % (capfirst(opts.verbose_name), object_id, strip_tags(repr(obj))), []]
perms_needed = sets.Set() perms_needed = sets.Set()
_get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1)
@ -1088,7 +1088,7 @@ def history(request, app_label, module_name, object_id):
c = Context(request, { c = Context(request, {
'title': 'Change history: %r' % obj, 'title': 'Change history: %r' % obj,
'action_list': action_list, 'action_list': action_list,
'module_name': meta.capfirst(opts.verbose_name_plural), 'module_name': capfirst(opts.verbose_name_plural),
'object': obj, 'object': obj,
}) })
return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8')