Ticket #7742: 7742.2008-08-26.3.diff

File 7742.2008-08-26.3.diff, 154.4 KB (added by Brian Rosner, 16 years ago)
  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    index 0b6dbf2..17213ea 100644
    a b def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_  
    8585                        perms_needed.add(related.opts.verbose_name)
    8686                        # We don't care about populating deleted_objects now.
    8787                        continue
    88                 if related.field.rel.edit_inline or not has_admin:
     88                if not has_admin:
    8989                    # Don't display link to edit, because it either has no
    9090                    # admin or is edited inline.
    9191                    nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []])
    def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_  
    101101            has_related_objs = False
    102102            for sub_obj in getattr(obj, rel_opts_name).all():
    103103                has_related_objs = True
    104                 if related.field.rel.edit_inline or not has_admin:
     104                if not has_admin:
    105105                    # Don't display link to edit, because it either has no
    106106                    # admin or is edited inline.
    107107                    nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []])
    def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_  
    132132
    133133        if has_related_objs:
    134134            for sub_obj in rel_objs.all():
    135                 if related.field.rel.edit_inline or not has_admin:
     135                if not has_admin:
    136136                    # Don't display link to edit, because it either has no
    137137                    # admin or is edited inline.
    138138                    nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
  • django/contrib/contenttypes/generic.py

    diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
    index 9c85af2..b033be5 100644
    a b  
    22Classes allowing "generic" relations through ContentType and object-id fields.
    33"""
    44
    5 from django import oldforms
    65from django.core.exceptions import ObjectDoesNotExist
    76from django.db import connection
    87from django.db.models import signals
    98from django.db import models
    109from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
    1110from django.db.models.loading import get_model
    12 from django.utils.functional import curry
    13 
    1411from django.forms import ModelForm
    1512from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance
    1613from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
     14from django.utils.encoding import smart_unicode
    1715
    1816class GenericForeignKey(object):
    1917    """
    class GenericRelation(RelatedField, Field):  
    120118        kwargs['serialize'] = False
    121119        Field.__init__(self, **kwargs)
    122120
    123     def get_manipulator_field_objs(self):
    124         choices = self.get_choices_default()
    125         return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
    126 
    127121    def get_choices_default(self):
    128122        return Field.get_choices(self, include_blank=False)
    129123
    130     def flatten_data(self, follow, obj = None):
    131         new_data = {}
    132         if obj:
    133             instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]
    134             new_data[self.name] = instance_ids
    135         return new_data
     124    def get_string_value(self, obj):
     125        qs = getattr(obj, self.name).all()
     126        return smart_unicode([instance._get_pk_val() for instance in qs])
    136127
    137128    def m2m_db_table(self):
    138129        return self.rel.to._meta.db_table
    class GenericRel(ManyToManyRel):  
    280271        self.to = to
    281272        self.related_name = related_name
    282273        self.limit_choices_to = limit_choices_to or {}
    283         self.edit_inline = False
    284274        self.symmetrical = symmetrical
    285275        self.multiple = True
    286276
    class BaseGenericInlineFormSet(BaseModelFormSet):  
    290280    """
    291281    ct_field_name = "content_type"
    292282    ct_fk_field_name = "object_id"
    293    
     283
    294284    def __init__(self, data=None, files=None, instance=None, save_as_new=None):
    295285        opts = self.model._meta
    296286        self.instance = instance
    class GenericStackedInline(GenericInlineModelAdmin):  
    385375
    386376class GenericTabularInline(GenericInlineModelAdmin):
    387377    template = 'admin/edit_inline/tabular.html'
    388 
  • django/core/serializers/base.py

    diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
    index bfd785a..8cfc412 100644
    a b class Serializer(object):  
    5757        """
    5858        Convert a field's value to a string.
    5959        """
    60         if isinstance(field, models.DateTimeField):
    61             d = datetime_safe.new_datetime(getattr(obj, field.name))
    62             value = d.strftime("%Y-%m-%d %H:%M:%S")
    63         else:
    64             value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
    65         return smart_unicode(value)
     60        return smart_unicode(field.get_string_value(obj))
    6661
    6762    def start_serialization(self):
    6863        """
  • django/db/models/__init__.py

    diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
    index cbd6855..7533da0 100644
    a b from django.db.models.base import Model  
    99from django.db.models.fields import *
    1010from django.db.models.fields.subclassing import SubfieldBase
    1111from django.db.models.fields.files import FileField, ImageField
    12 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
     12from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel
    1313from django.db.models import signals
    1414
    1515# Admin stages.
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index 115d82b..e858319 100644
    a b try:  
    88except NameError:
    99    from sets import Set as set     # Python 2.3 fallback.
    1010
    11 import django.db.models.manipulators    # Imported to register signal handler.
    12 import django.db.models.manager         # Ditto.
     11import django.db.models.manager     # Imported to register signal handler.
    1312from django.core import validators
    1413from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
    1514from django.db.models.fields import AutoField
    class Model(object):  
    320319
    321320        # First, try an UPDATE. If that doesn't update anything, do an INSERT.
    322321        pk_val = self._get_pk_val(meta)
    323         # Note: the comparison with '' is required for compatibility with
    324         # oldforms-style model creation.
    325         pk_set = pk_val is not None and smart_unicode(pk_val) != u''
     322        pk_set = pk_val is not None
    326323        record_exists = True
    327324        manager = cls._default_manager
    328325        if pk_set:
  • django/db/models/fields/__init__.py

    diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
    index b8d0567..5abd25a 100644
    a b from django.db.models.query_utils import QueryWrapper  
    1313from django.dispatch import dispatcher
    1414from django.conf import settings
    1515from django.core import validators
    16 from django import oldforms
    1716from django import forms
    1817from django.core.exceptions import ObjectDoesNotExist
    1918from django.utils.datastructures import DictWrapper
    BLANK_CHOICE_NONE = [("", "None")]  
    3433class FieldDoesNotExist(Exception):
    3534    pass
    3635
    37 def manipulator_validator_unique(f, opts, self, field_data, all_data):
    38     "Validates that the value is unique for this field."
    39     lookup_type = f.get_validator_unique_lookup_type()
    40     try:
    41         old_obj = self.manager.get(**{lookup_type: field_data})
    42     except ObjectDoesNotExist:
    43         return
    44     if getattr(self, 'original_object', None) and self.original_object._get_pk_val() == old_obj._get_pk_val():
    45         return
    46     raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name}
    47 
    4836# A guide to Field parameters:
    4937#
    5038#   * name:      The name of the field specifed in the model.
    class Field(object):  
    7361
    7462    def __init__(self, verbose_name=None, name=None, primary_key=False,
    7563            max_length=None, unique=False, blank=False, null=False,
    76             db_index=False, core=False, rel=None, default=NOT_PROVIDED,
    77             editable=True, serialize=True, unique_for_date=None,
    78             unique_for_month=None, unique_for_year=None, validator_list=None,
    79             choices=None, help_text='', db_column=None, db_tablespace=None,
     64            db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
     65            serialize=True, unique_for_date=None, unique_for_month=None,
     66            unique_for_year=None, choices=None,
     67            help_text='', db_column=None, db_tablespace=None,
    8068            auto_created=False):
    8169        self.name = name
    8270        self.verbose_name = verbose_name
    class Field(object):  
    8775        # option whenever '' is a possible value.
    8876        if self.empty_strings_allowed and connection.features.interprets_empty_strings_as_nulls:
    8977            self.null = True
    90         self.core, self.rel, self.default = core, rel, default
     78        self.rel, self.default = rel, default
    9179        self.editable = editable
    9280        self.serialize = serialize
    93         self.validator_list = validator_list or []
    9481        self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
    9582        self.unique_for_year = unique_for_year
    9683        self._choices = choices or []
    class Field(object):  
    252239            return None
    253240        return ""
    254241
    255     def get_manipulator_field_names(self, name_prefix):
    256         """
    257         Returns a list of field names that this object adds to the manipulator.
    258         """
    259         return [name_prefix + self.name]
    260 
    261     def prepare_field_objs_and_params(self, manipulator, name_prefix):
    262         params = {'validator_list': self.validator_list[:]}
    263         if self.max_length and not self.choices: # Don't give SelectFields a max_length parameter.
    264             params['max_length'] = self.max_length
    265 
    266         if self.choices:
    267             field_objs = [oldforms.SelectField]
    268 
    269             params['choices'] = self.get_flatchoices()
    270         else:
    271             field_objs = self.get_manipulator_field_objs()
    272         return (field_objs, params)
    273 
    274     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    275         """
    276         Returns a list of oldforms.FormField instances for this field. It
    277         calculates the choices at runtime, not at compile time.
    278 
    279         name_prefix is a prefix to prepend to the "field_name" argument.
    280         rel is a boolean specifying whether this field is in a related context.
    281         """
    282         field_objs, params = self.prepare_field_objs_and_params(manipulator, name_prefix)
    283 
    284         # Add the "unique" validator(s).
    285         for field_name_list in opts.unique_together:
    286             if field_name_list[0] == self.name:
    287                 params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
    288 
    289         # Add the "unique for..." validator(s).
    290         if self.unique_for_date:
    291             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
    292         if self.unique_for_month:
    293             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
    294         if self.unique_for_year:
    295             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
    296         if self.unique and not rel:
    297             params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
    298 
    299         # Only add is_required=True if the field cannot be blank. Primary keys
    300         # are a special case, and fields in a related context should set this
    301         # as False, because they'll be caught by a separate validator --
    302         # RequiredIfOtherFieldGiven.
    303         params['is_required'] = not self.blank and not self.primary_key and not rel
    304 
    305         # BooleanFields (CheckboxFields) are a special case. They don't take
    306         # is_required.
    307         if isinstance(self, BooleanField):
    308             del params['is_required']
    309 
    310         # If this field is in a related context, check whether any other fields
    311         # in the related object have core=True. If so, add a validator --
    312         # RequiredIfOtherFieldsGiven -- to this FormField.
    313         if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
    314             # First, get the core fields, if any.
    315             core_field_names = []
    316             for f in opts.fields:
    317                 if f.core and f != self:
    318                     core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    319             # Now, if there are any, add the validator to this FormField.
    320             if core_field_names:
    321                 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, ugettext_lazy("This field is required.")))
    322 
    323         # Finally, add the field_names.
    324         field_names = self.get_manipulator_field_names(name_prefix)
    325         return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
    326 
    327242    def get_validator_unique_lookup_type(self):
    328243        return '%s__exact' % self.name
    329244
    330     def get_manipulator_new_data(self, new_data, rel=False):
    331         """
    332         Given the full new_data dictionary (from the manipulator), returns this
    333         field's data.
    334         """
    335         if rel:
    336             return new_data.get(self.name, [self.get_default()])[0]
    337         val = new_data.get(self.name, self.get_default())
    338         if not self.empty_strings_allowed and val == '' and self.null:
    339             val = None
    340         return val
    341 
    342245    def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
    343246        """Returns choices with a default blank choices included, for use
    344247        as SelectField choices for this field."""
    class Field(object):  
    366269        else:
    367270            return self.get_default()
    368271
    369     def flatten_data(self, follow, obj=None):
     272    def get_string_value(self, obj):
    370273        """
    371         Returns a dictionary mapping the field's manipulator field names to its
    372         "flattened" string values for the admin view. obj is the instance to
    373         extract the values from.
     274        Returns a string value of this field from the passed obj.
    374275        """
    375         return {self.attname: self._get_val_from_obj(obj)}
    376 
    377     def get_follow(self, override=None):
    378         if override != None:
    379             return override
    380         else:
    381             return self.editable
     276        return smart_unicode(self._get_val_from_obj(obj))
    382277
    383278    def bind(self, fieldmapping, original, bound_field_class):
    384279        return bound_field_class(self, fieldmapping, original)
    class AutoField(Field):  
    439334            return None
    440335        return int(value)
    441336
    442     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    443         if not rel:
    444             return [] # Don't add a FormField unless it's in a related context.
    445         return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    446 
    447     def get_manipulator_field_objs(self):
    448         return [oldforms.HiddenField]
    449 
    450     def get_manipulator_new_data(self, new_data, rel=False):
    451         # Never going to be called
    452         # Not in main change pages
    453         # ignored in related context
    454         if not rel:
    455             return None
    456         return Field.get_manipulator_new_data(self, new_data, rel)
    457 
    458337    def contribute_to_class(self, cls, name):
    459338        assert not cls._meta.has_auto_field, "A model can't have more than one AutoField."
    460339        super(AutoField, self).contribute_to_class(cls, name)
    class BooleanField(Field):  
    485364            return None
    486365        return bool(value)
    487366
    488     def get_manipulator_field_objs(self):
    489         return [oldforms.CheckboxField]
    490 
    491367    def formfield(self, **kwargs):
    492368        defaults = {'form_class': forms.BooleanField}
    493369        defaults.update(kwargs)
    494370        return super(BooleanField, self).formfield(**defaults)
    495371
    496372class CharField(Field):
    497     def get_manipulator_field_objs(self):
    498         return [oldforms.TextField]
    499 
    500373    def get_internal_type(self):
    501374        return "CharField"
    502375
    class CharField(Field):  
    517390
    518391# TODO: Maybe move this into contrib, because it's specialized.
    519392class CommaSeparatedIntegerField(CharField):
    520     def get_manipulator_field_objs(self):
    521         return [oldforms.CommaSeparatedIntegerField]
     393    pass
    522394
    523395class DateField(Field):
    524396    empty_strings_allowed = False
    class DateField(Field):  
    562434            setattr(cls, 'get_previous_by_%s' % self.name,
    563435                curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=False))
    564436
    565     # Needed because of horrible auto_now[_add] behaviour wrt. editable
    566     def get_follow(self, override=None):
    567         if override != None:
    568             return override
    569         else:
    570             return self.editable or self.auto_now or self.auto_now_add
    571 
    572437    def get_db_prep_lookup(self, lookup_type, value):
    573438        # For "__month" and "__day" lookups, convert the value to a string so
    574439        # the database backend always sees a consistent type.
    class DateField(Field):  
    580445        # Casts dates into the format expected by the backend
    581446        return connection.ops.value_to_db_date(self.to_python(value))
    582447
    583     def get_manipulator_field_objs(self):
    584         return [oldforms.DateField]
    585 
    586     def flatten_data(self, follow, obj=None):
     448    def get_string_value(self, obj):
    587449        val = self._get_val_from_obj(obj)
    588450        if val is None:
    589451            data = ''
    590452        else:
    591453            data = datetime_safe.new_date(val).strftime("%Y-%m-%d")
    592         return {self.attname: data}
     454        return data
    593455
    594456    def formfield(self, **kwargs):
    595457        defaults = {'form_class': forms.DateField}
    class DateTimeField(DateField):  
    639501        # Casts dates into the format expected by the backend
    640502        return connection.ops.value_to_db_datetime(self.to_python(value))
    641503
    642     def get_manipulator_field_objs(self):
    643         return [oldforms.DateField, oldforms.TimeField]
    644 
    645     def get_manipulator_field_names(self, name_prefix):
    646         return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
    647 
    648     def get_manipulator_new_data(self, new_data, rel=False):
    649         date_field, time_field = self.get_manipulator_field_names('')
    650         if rel:
    651             d = new_data.get(date_field, [None])[0]
    652             t = new_data.get(time_field, [None])[0]
    653         else:
    654             d = new_data.get(date_field, None)
    655             t = new_data.get(time_field, None)
    656         if d is not None and t is not None:
    657             return datetime.datetime.combine(d, t)
    658         return self.get_default()
    659 
    660     def flatten_data(self,follow, obj = None):
     504    def get_string_value(self, obj):
    661505        val = self._get_val_from_obj(obj)
    662         date_field, time_field = self.get_manipulator_field_names('')
    663506        if val is None:
    664             date_data = time_data = ''
     507            data = ''
    665508        else:
    666509            d = datetime_safe.new_datetime(val)
    667             date_data = d.strftime('%Y-%m-%d')
    668             time_data = d.strftime('%H:%M:%S')
    669         return {date_field: date_data, time_field: time_data}
     510            data = d.strftime('%Y-%m-%d %H:%M:%S')
     511        return data
    670512
    671513    def formfield(self, **kwargs):
    672514        defaults = {'form_class': forms.DateTimeField}
    class DecimalField(Field):  
    715557        return connection.ops.value_to_db_decimal(self.to_python(value),
    716558                self.max_digits, self.decimal_places)
    717559
    718     def get_manipulator_field_objs(self):
    719         return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
    720 
    721560    def formfield(self, **kwargs):
    722561        defaults = {
    723562            'max_digits': self.max_digits,
    class EmailField(CharField):  
    732571        kwargs['max_length'] = kwargs.get('max_length', 75)
    733572        CharField.__init__(self, *args, **kwargs)
    734573
    735     def get_manipulator_field_objs(self):
    736         return [oldforms.EmailField]
    737 
    738574    def formfield(self, **kwargs):
    739575        defaults = {'form_class': forms.EmailField}
    740576        defaults.update(kwargs)
    class FilePathField(Field):  
    756592        defaults.update(kwargs)
    757593        return super(FilePathField, self).formfield(**defaults)
    758594
    759     def get_manipulator_field_objs(self):
    760         return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
    761 
    762595    def get_internal_type(self):
    763596        return "FilePathField"
    764597
    class FloatField(Field):  
    770603            return None
    771604        return float(value)
    772605
    773     def get_manipulator_field_objs(self):
    774         return [oldforms.FloatField]
    775 
    776606    def get_internal_type(self):
    777607        return "FloatField"
    778608
    class IntegerField(Field):  
    788618            return None
    789619        return int(value)
    790620
    791     def get_manipulator_field_objs(self):
    792         return [oldforms.IntegerField]
    793 
    794621    def get_internal_type(self):
    795622        return "IntegerField"
    796623
    class IPAddressField(Field):  
    813640        kwargs['max_length'] = 15
    814641        Field.__init__(self, *args, **kwargs)
    815642
    816     def get_manipulator_field_objs(self):
    817         return [oldforms.IPAddressField]
    818 
    819643    def get_internal_type(self):
    820644        return "IPAddressField"
    821645
    class NullBooleanField(Field):  
    845669            return None
    846670        return bool(value)
    847671
    848     def get_manipulator_field_objs(self):
    849         return [oldforms.NullBooleanField]
    850 
    851672    def formfield(self, **kwargs):
    852673        defaults = {
    853674            'form_class': forms.NullBooleanField,
    class NullBooleanField(Field):  
    858679        return super(NullBooleanField, self).formfield(**defaults)
    859680
    860681class PhoneNumberField(Field):
    861     def get_manipulator_field_objs(self):
    862         return [oldforms.PhoneNumberField]
    863 
    864682    def get_internal_type(self):
    865683        return "PhoneNumberField"
    866684
    class PhoneNumberField(Field):  
    871689        return super(PhoneNumberField, self).formfield(**defaults)
    872690
    873691class PositiveIntegerField(IntegerField):
    874     def get_manipulator_field_objs(self):
    875         return [oldforms.PositiveIntegerField]
    876 
    877692    def get_internal_type(self):
    878693        return "PositiveIntegerField"
    879694
    class PositiveIntegerField(IntegerField):  
    883698        return super(PositiveIntegerField, self).formfield(**defaults)
    884699
    885700class PositiveSmallIntegerField(IntegerField):
    886     def get_manipulator_field_objs(self):
    887         return [oldforms.PositiveSmallIntegerField]
    888 
    889701    def get_internal_type(self):
    890702        return "PositiveSmallIntegerField"
    891703
    class PositiveSmallIntegerField(IntegerField):  
    897709class SlugField(CharField):
    898710    def __init__(self, *args, **kwargs):
    899711        kwargs['max_length'] = kwargs.get('max_length', 50)
    900         kwargs.setdefault('validator_list', []).append(validators.isSlug)
    901712        # Set db_index=True unless it's been set manually.
    902713        if 'db_index' not in kwargs:
    903714            kwargs['db_index'] = True
    class SlugField(CharField):  
    914725        return super(SlugField, self).formfield(**defaults)
    915726
    916727class SmallIntegerField(IntegerField):
    917     def get_manipulator_field_objs(self):
    918         return [oldforms.SmallIntegerField]
    919 
    920728    def get_internal_type(self):
    921729        return "SmallIntegerField"
    922730
    923731class TextField(Field):
    924     def get_manipulator_field_objs(self):
    925         return [oldforms.LargeTextField]
    926 
    927732    def get_internal_type(self):
    928733        return "TextField"
    929734
    class TimeField(Field):  
    984789        # Casts times into the format expected by the backend
    985790        return connection.ops.value_to_db_time(self.to_python(value))
    986791
    987     def get_manipulator_field_objs(self):
    988         return [oldforms.TimeField]
    989 
    990     def flatten_data(self,follow, obj = None):
     792    def get_string_value(self, obj):
    991793        val = self._get_val_from_obj(obj)
    992         return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
     794        if val is None:
     795            data = ''
     796        else:
     797            data = val.strftime("%H:%M:%S")
     798        return data
    993799
    994800    def formfield(self, **kwargs):
    995801        defaults = {'form_class': forms.TimeField}
    class TimeField(Field):  
    999805class URLField(CharField):
    1000806    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
    1001807        kwargs['max_length'] = kwargs.get('max_length', 200)
    1002         if verify_exists:
    1003             kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
    1004808        self.verify_exists = verify_exists
    1005809        CharField.__init__(self, verbose_name, name, **kwargs)
    1006810
    1007     def get_manipulator_field_objs(self):
    1008         return [oldforms.URLField]
    1009 
    1010811    def formfield(self, **kwargs):
    1011812        defaults = {'form_class': forms.URLField, 'verify_exists': self.verify_exists}
    1012813        defaults.update(kwargs)
    1013814        return super(URLField, self).formfield(**defaults)
    1014815
    1015816class USStateField(Field):
    1016     def get_manipulator_field_objs(self):
    1017         return [oldforms.USStateField]
    1018 
    1019817    def get_internal_type(self):
    1020818        return "USStateField"
    1021819
    class XMLField(TextField):  
    1029827    def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
    1030828        self.schema_path = schema_path
    1031829        Field.__init__(self, verbose_name, name, **kwargs)
    1032 
    1033     def get_manipulator_field_objs(self):
    1034         return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
    1035 
  • django/db/models/fields/files.py

    diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
    index 935dee6..33b6ee5 100644
    a b from django.utils.functional import curry  
    1111from django.db.models import signals
    1212from django.utils.encoding import force_unicode, smart_str
    1313from django.utils.translation import ugettext_lazy, ugettext as _
    14 from django import oldforms
    1514from django import forms
    1615from django.core import validators
    1716from django.db.models.loading import cache
    class FileField(Field):  
    126125    attr_class = FieldFile
    127126
    128127    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
    129         for arg in ('core', 'primary_key', 'unique'):
     128        for arg in ('primary_key', 'unique'):
    130129            if arg in kwargs:
    131130                raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
    132131
    class FileField(Field):  
    153152            return None
    154153        return unicode(value)
    155154
    156     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    157         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    158         if not self.blank:
    159             if rel:
    160                 # This validator makes sure FileFields work in a related context.
    161                 class RequiredFileField(object):
    162                     def __init__(self, other_field_names, other_file_field_name):
    163                         self.other_field_names = other_field_names
    164                         self.other_file_field_name = other_file_field_name
    165                         self.always_test = True
    166                     def __call__(self, field_data, all_data):
    167                         if not all_data.get(self.other_file_field_name, False):
    168                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
    169                             c(field_data, all_data)
    170                 # First, get the core fields, if any.
    171                 core_field_names = []
    172                 for f in opts.fields:
    173                     if f.core and f != self:
    174                         core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    175                 # Now, if there are any, add the validator to this FormField.
    176                 if core_field_names:
    177                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
    178             else:
    179                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
    180                 v.always_test = True
    181                 field_list[0].validator_list.append(v)
    182                 field_list[0].is_required = field_list[1].is_required = False
    183 
    184         # If the raw path is passed in, validate it's under the MEDIA_ROOT.
    185         def isWithinMediaRoot(field_data, all_data):
    186             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
    187             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
    188                 raise validators.ValidationError(_("Enter a valid filename."))
    189         field_list[1].validator_list.append(isWithinMediaRoot)
    190         return field_list
    191 
    192155    def contribute_to_class(self, cls, name):
    193156        super(FileField, self).contribute_to_class(cls, name)
    194157        setattr(cls, self.name, FileDescriptor(self))
    class FileField(Field):  
    206169            # Otherwise, just close the file, so it doesn't tie up resources.
    207170            file.close()
    208171
    209     def get_manipulator_field_objs(self):
    210         return [oldforms.FileUploadField, oldforms.HiddenField]
    211 
    212     def get_manipulator_field_names(self, name_prefix):
    213         return [name_prefix + self.name + '_file', name_prefix + self.name]
    214 
    215     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    216         upload_field_name = self.get_manipulator_field_names('')[0]
     172    def save_file(self, new_data, new_object, original_object, change, rel,
     173                  save=True):
     174        upload_field_name = self.name + '_file'
    217175        if new_data.get(upload_field_name, False):
    218176            if rel:
    219177                file = new_data[upload_field_name][0]
    class ImageField(FileField):  
    282240        self.width_field, self.height_field = width_field, height_field
    283241        FileField.__init__(self, verbose_name, name, **kwargs)
    284242
    285     def get_manipulator_field_objs(self):
    286         return [oldforms.ImageUploadField, oldforms.HiddenField]
    287 
    288243    def formfield(self, **kwargs):
    289244        defaults = {'form_class': forms.ImageField}
    290245        defaults.update(kwargs)
  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index 7601911..c422884 100644
    a b from django.db.models.fields import AutoField, Field, IntegerField, PositiveInte  
    44from django.db.models.related import RelatedObject
    55from django.db.models.query import QuerySet
    66from django.db.models.query_utils import QueryWrapper
     7from django.utils.encoding import smart_unicode
    78from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _
    89from django.utils.functional import curry
    910from django.core import validators
    10 from django import oldforms
    1111from django import forms
    1212
    1313try:
    try:  
    1515except NameError:
    1616    from sets import Set as set   # Python 2.3 fallback
    1717
    18 # Values for Relation.edit_inline.
    19 TABULAR, STACKED = 1, 2
    20 
    2118RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
    2219
    2320pending_lookups = {}
    def do_pending_lookups(sender, **kwargs):  
    8380
    8481signals.class_prepared.connect(do_pending_lookups)
    8582
    86 def manipulator_valid_rel_key(f, self, field_data, all_data):
    87     "Validates that the value is a valid foreign key"
    88     klass = f.rel.to
    89     try:
    90         klass._default_manager.get(**{f.rel.field_name: field_data})
    91     except klass.DoesNotExist:
    92         raise validators.ValidationError, _("Please enter a valid %s.") % f.verbose_name
    93 
    9483#HACK
    9584class RelatedField(object):
    9685    def contribute_to_class(self, cls, name):
    class ReverseManyRelatedObjectsDescriptor(object):  
    580569        manager.add(*value)
    581570
    582571class ManyToOneRel(object):
    583     def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
    584             max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
    585             related_name=None, limit_choices_to=None, lookup_overrides=None,
    586             parent_link=False):
     572    def __init__(self, to, field_name, related_name=None,
     573            limit_choices_to=None, lookup_overrides=None, parent_link=False):
    587574        try:
    588575            to._meta
    589576        except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
    590577            assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
    591578        self.to, self.field_name = to, field_name
    592         self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
    593         self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
    594         self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
     579        self.related_name = related_name
    595580        if limit_choices_to is None:
    596581            limit_choices_to = {}
    597582        self.limit_choices_to = limit_choices_to
    class ManyToOneRel(object):  
    611596        return data[0]
    612597
    613598class OneToOneRel(ManyToOneRel):
    614     def __init__(self, to, field_name, num_in_admin=0, min_num_in_admin=None,
    615             max_num_in_admin=None, num_extra_on_change=None, edit_inline=False,
    616             related_name=None, limit_choices_to=None, lookup_overrides=None,
    617             parent_link=False):
     599    def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
     600            lookup_overrides=None, parent_link=False):
    618601        # NOTE: *_num_in_admin and num_extra_on_change are intentionally
    619602        # ignored here. We accept them as parameters only to match the calling
    620603        # signature of ManyToOneRel.__init__().
    621         super(OneToOneRel, self).__init__(to, field_name, num_in_admin,
    622                 edit_inline=edit_inline, related_name=related_name,
    623                 limit_choices_to=limit_choices_to,
     604        super(OneToOneRel, self).__init__(to, field_name,
     605                related_name=related_name, limit_choices_to=limit_choices_to,
    624606                lookup_overrides=lookup_overrides, parent_link=parent_link)
    625607        self.multiple = False
    626608
    627609class ManyToManyRel(object):
    628     def __init__(self, to, num_in_admin=0, related_name=None,
    629         limit_choices_to=None, symmetrical=True, through=None):
     610    def __init__(self, to, related_name=None, limit_choices_to=None,
     611            symmetrical=True, through=None):
    630612        self.to = to
    631         self.num_in_admin = num_in_admin
    632613        self.related_name = related_name
    633614        if limit_choices_to is None:
    634615            limit_choices_to = {}
    635616        self.limit_choices_to = limit_choices_to
    636         self.edit_inline = False
    637617        self.symmetrical = symmetrical
    638618        self.multiple = True
    639619        self.through = through
    class ForeignKey(RelatedField, Field):  
    651631        kwargs['verbose_name'] = kwargs.get('verbose_name', None)
    652632
    653633        kwargs['rel'] = rel_class(to, to_field,
    654             num_in_admin=kwargs.pop('num_in_admin', 3),
    655             min_num_in_admin=kwargs.pop('min_num_in_admin', None),
    656             max_num_in_admin=kwargs.pop('max_num_in_admin', None),
    657             num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
    658             edit_inline=kwargs.pop('edit_inline', False),
    659634            related_name=kwargs.pop('related_name', None),
    660635            limit_choices_to=kwargs.pop('limit_choices_to', None),
    661636            lookup_overrides=kwargs.pop('lookup_overrides', None),
    class ForeignKey(RelatedField, Field):  
    670645    def get_validator_unique_lookup_type(self):
    671646        return '%s__%s__exact' % (self.name, self.rel.get_related_field().name)
    672647
    673     def prepare_field_objs_and_params(self, manipulator, name_prefix):
    674         params = {'validator_list': self.validator_list[:], 'member_name': name_prefix + self.attname}
    675         if self.null:
    676             field_objs = [oldforms.NullSelectField]
    677         else:
    678             field_objs = [oldforms.SelectField]
    679         params['choices'] = self.get_choices_default()
    680         return field_objs, params
    681 
    682648    def get_default(self):
    683649        "Here we check if the default value is an object and return the to_field if so."
    684650        field_default = super(ForeignKey, self).get_default()
    class ForeignKey(RelatedField, Field):  
    686652            return getattr(field_default, self.rel.get_related_field().attname)
    687653        return field_default
    688654
    689     def get_manipulator_field_objs(self):
    690         rel_field = self.rel.get_related_field()
    691         return [oldforms.IntegerField]
    692 
    693655    def get_db_prep_save(self, value):
    694656        if value == '' or value == None:
    695657            return None
    696658        else:
    697659            return self.rel.get_related_field().get_db_prep_save(value)
    698660
    699     def flatten_data(self, follow, obj=None):
     661    def get_string_value(self, obj):
    700662        if not obj:
    701663            # In required many-to-one fields with only one available choice,
    702664            # select that one available choice. Note: For SelectFields
    class ForeignKey(RelatedField, Field):  
    705667            if not self.blank and self.choices:
    706668                choice_list = self.get_choices_default()
    707669                if len(choice_list) == 2:
    708                     return {self.attname: choice_list[1][0]}
    709         return Field.flatten_data(self, follow, obj)
     670                    return smart_unicode(choice_list[1][0])
     671        return Field.get_string_value(self, obj)
    710672
    711673    def contribute_to_class(self, cls, name):
    712674        super(ForeignKey, self).contribute_to_class(cls, name)
    class OneToOneField(ForeignKey):  
    744706    """
    745707    def __init__(self, to, to_field=None, **kwargs):
    746708        kwargs['unique'] = True
    747         if 'num_in_admin' not in kwargs:
    748             kwargs['num_in_admin'] = 0
    749709        super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
    750710
    751711    def contribute_to_related_class(self, cls, related):
    class ManyToManyField(RelatedField, Field):  
    768728
    769729        kwargs['verbose_name'] = kwargs.get('verbose_name', None)
    770730        kwargs['rel'] = ManyToManyRel(to,
    771             num_in_admin=kwargs.pop('num_in_admin', 0),
    772731            related_name=kwargs.pop('related_name', None),
    773732            limit_choices_to=kwargs.pop('limit_choices_to', None),
    774733            symmetrical=kwargs.pop('symmetrical', True),
    class ManyToManyField(RelatedField, Field):  
    786745        msg = ugettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.')
    787746        self.help_text = string_concat(self.help_text, ' ', msg)
    788747
    789     def get_manipulator_field_objs(self):
    790         choices = self.get_choices_default()
    791         return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
    792 
    793748    def get_choices_default(self):
    794749        return Field.get_choices(self, include_blank=False)
    795750
    class ManyToManyField(RelatedField, Field):  
    869824                'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys),
    870825            }
    871826
    872     def flatten_data(self, follow, obj = None):
    873         new_data = {}
     827    def get_string_value(self, obj):
     828        data = ''
    874829        if obj:
    875             instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]
    876             new_data[self.name] = instance_ids
     830            qs = getattr(obj, self.name).all()
     831            data = [instance._get_pk_val() for instance in qs]
    877832        else:
    878833            # In required many-to-many fields with only one available choice,
    879834            # select that one available choice.
    880             if not self.blank and not self.rel.edit_inline:
     835            if not self.blank:
    881836                choices_list = self.get_choices_default()
    882837                if len(choices_list) == 1:
    883                     new_data[self.name] = [choices_list[0][0]]
    884         return new_data
     838                    data = [choices_list[0][0]]
     839        return smart_unicode(data)
    885840
    886841    def contribute_to_class(self, cls, name):
    887842        super(ManyToManyField, self).contribute_to_class(cls, name)
  • deleted file django/db/models/manipulators.py

    diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py
    deleted file mode 100644
    index c657d01..0000000
    + -  
    1 from django.core.exceptions import ObjectDoesNotExist
    2 from django import oldforms
    3 from django.core import validators
    4 from django.db.models.fields import AutoField
    5 from django.db.models.fields.files import FileField
    6 from django.db.models import signals
    7 from django.utils.functional import curry
    8 from django.utils.datastructures import DotExpandedDict
    9 from django.utils.text import capfirst
    10 from django.utils.encoding import smart_str
    11 from django.utils.translation import ugettext as _
    12 from django.utils import datetime_safe
    13 
    14 def add_manipulators(sender, **kwargs):
    15     cls = sender
    16     cls.add_to_class('AddManipulator', AutomaticAddManipulator)
    17     cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator)
    18 
    19 signals.class_prepared.connect(add_manipulators)
    20 
    21 class ManipulatorDescriptor(object):
    22     # This class provides the functionality that makes the default model
    23     # manipulators (AddManipulator and ChangeManipulator) available via the
    24     # model class.
    25     def __init__(self, name, base):
    26         self.man = None # Cache of the manipulator class.
    27         self.name = name
    28         self.base = base
    29 
    30     def __get__(self, instance, model=None):
    31         if instance != None:
    32             raise AttributeError, "Manipulator cannot be accessed via instance"
    33         else:
    34             if not self.man:
    35                 # Create a class that inherits from the "Manipulator" class
    36                 # given in the model class (if specified) and the automatic
    37                 # manipulator.
    38                 bases = [self.base]
    39                 if hasattr(model, 'Manipulator'):
    40                     bases = [model.Manipulator] + bases
    41                 self.man = type(self.name, tuple(bases), {})
    42                 self.man._prepare(model)
    43             return self.man
    44 
    45 class AutomaticManipulator(oldforms.Manipulator):
    46     def _prepare(cls, model):
    47         cls.model = model
    48         cls.manager = model._default_manager
    49         cls.opts = model._meta
    50         for field_name_list in cls.opts.unique_together:
    51             setattr(cls, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, cls.opts))
    52         for f in cls.opts.fields:
    53             if f.unique_for_date:
    54                 setattr(cls, 'isUnique%sFor%s' % (f.name, f.unique_for_date), curry(manipulator_validator_unique_for_date, f, cls.opts.get_field(f.unique_for_date), cls.opts, 'date'))
    55             if f.unique_for_month:
    56                 setattr(cls, 'isUnique%sFor%s' % (f.name, f.unique_for_month), curry(manipulator_validator_unique_for_date, f, cls.opts.get_field(f.unique_for_month), cls.opts, 'month'))
    57             if f.unique_for_year:
    58                 setattr(cls, 'isUnique%sFor%s' % (f.name, f.unique_for_year), curry(manipulator_validator_unique_for_date, f, cls.opts.get_field(f.unique_for_year), cls.opts, 'year'))
    59     _prepare = classmethod(_prepare)
    60 
    61     def contribute_to_class(cls, other_cls, name):
    62         setattr(other_cls, name, ManipulatorDescriptor(name, cls))
    63     contribute_to_class = classmethod(contribute_to_class)
    64 
    65     def __init__(self, follow=None):
    66         self.follow = self.opts.get_follow(follow)
    67         self.fields = []
    68 
    69         for f in self.opts.fields + self.opts.many_to_many:
    70             if self.follow.get(f.name, False):
    71                 self.fields.extend(f.get_manipulator_fields(self.opts, self, self.change))
    72 
    73         # Add fields for related objects.
    74         for f in self.opts.get_all_related_objects():
    75             if self.follow.get(f.name, False):
    76                 fol = self.follow[f.name]
    77                 self.fields.extend(f.get_manipulator_fields(self.opts, self, self.change, fol))
    78 
    79         # Add field for ordering.
    80         if self.change and self.opts.get_ordered_objects():
    81             self.fields.append(oldforms.CommaSeparatedIntegerField(field_name="order_"))
    82 
    83     def save(self, new_data):
    84         # TODO: big cleanup when core fields go -> use recursive manipulators.
    85         params = {}
    86         for f in self.opts.fields:
    87             # Fields with auto_now_add should keep their original value in the change stage.
    88             auto_now_add = self.change and getattr(f, 'auto_now_add', False)
    89             if self.follow.get(f.name, None) and not auto_now_add:
    90                 param = f.get_manipulator_new_data(new_data)
    91             else:
    92                 if self.change:
    93                     param = getattr(self.original_object, f.attname)
    94                 else:
    95                     param = f.get_default()
    96             params[f.attname] = param
    97 
    98         if self.change:
    99             params[self.opts.pk.attname] = self.obj_key
    100 
    101         # First, create the basic object itself.
    102         new_object = self.model(**params)
    103 
    104         # Now that the object's been created, save any uploaded files.
    105         for f in self.opts.fields:
    106             if isinstance(f, FileField):
    107                 f.save_file(new_data, new_object, self.change and self.original_object or None, self.change, rel=False, save=False)
    108 
    109         # Now save the object
    110         new_object.save()
    111 
    112         # Calculate which primary fields have changed.
    113         if self.change:
    114             self.fields_added, self.fields_changed, self.fields_deleted = [], [], []
    115             for f in self.opts.fields:
    116                 if not f.primary_key and smart_str(getattr(self.original_object, f.attname)) != smart_str(getattr(new_object, f.attname)):
    117                     self.fields_changed.append(f.verbose_name)
    118 
    119         # Save many-to-many objects. Example: Set sites for a poll.
    120         for f in self.opts.many_to_many:
    121             if self.follow.get(f.name, None):
    122                 if not f.rel.edit_inline:
    123                     new_vals = new_data.getlist(f.name)
    124                     # First, clear the existing values.
    125                     rel_manager = getattr(new_object, f.name)
    126                     rel_manager.clear()
    127                     # Then, set the new values.
    128                     for n in new_vals:
    129                         rel_manager.add(f.rel.to._default_manager.get(pk=n))
    130                     # TODO: Add to 'fields_changed'
    131 
    132         expanded_data = DotExpandedDict(dict(new_data))
    133         # Save many-to-one objects. Example: Add the Choice objects for a Poll.
    134         for related in self.opts.get_all_related_objects():
    135             # Create obj_list, which is a DotExpandedDict such as this:
    136             # [('0', {'id': ['940'], 'choice': ['This is the first choice']}),
    137             #  ('1', {'id': ['941'], 'choice': ['This is the second choice']}),
    138             #  ('2', {'id': [''], 'choice': ['']})]
    139             child_follow = self.follow.get(related.name, None)
    140 
    141             if child_follow:
    142                 obj_list = expanded_data.get(related.var_name, {}).items()
    143                 if not obj_list:
    144                     continue
    145 
    146                 obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
    147 
    148                 # For each related item...
    149                 for _, rel_new_data in obj_list:
    150 
    151                     params = {}
    152 
    153                     # Keep track of which core=True fields were provided.
    154                     # If all core fields were given, the related object will be saved.
    155                     # If none of the core fields were given, the object will be deleted.
    156                     # If some, but not all, of the fields were given, the validator would
    157                     # have caught that.
    158                     all_cores_given, all_cores_blank = True, True
    159 
    160                     # Get a reference to the old object. We'll use it to compare the
    161                     # old to the new, to see which fields have changed.
    162                     old_rel_obj = None
    163                     if self.change:
    164                         if rel_new_data[related.opts.pk.name][0]:
    165                             try:
    166                                 old_rel_obj = getattr(self.original_object, related.get_accessor_name()).get(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.attname][0]})
    167                             except ObjectDoesNotExist:
    168                                 pass
    169 
    170                     for f in related.opts.fields:
    171                         if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
    172                             all_cores_given = False
    173                         elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
    174                             all_cores_blank = False
    175                         # If this field isn't editable, give it the same value it had
    176                         # previously, according to the given ID. If the ID wasn't
    177                         # given, use a default value. FileFields are also a special
    178                         # case, because they'll be dealt with later.
    179 
    180                         if f == related.field:
    181                             param = getattr(new_object, related.field.rel.get_related_field().attname)
    182                         elif (not self.change) and isinstance(f, AutoField):
    183                             param = None
    184                         elif self.change and (isinstance(f, FileField) or not child_follow.get(f.name, None)):
    185                             if old_rel_obj:
    186                                 param = getattr(old_rel_obj, f.column)
    187                             else:
    188                                 param = f.get_default()
    189                         else:
    190                             param = f.get_manipulator_new_data(rel_new_data, rel=True)
    191                         if param != None:
    192                             params[f.attname] = param
    193 
    194                     # Create the related item.
    195                     new_rel_obj = related.model(**params)
    196 
    197                     # If all the core fields were provided (non-empty), save the item.
    198                     if all_cores_given:
    199                         new_rel_obj.save()
    200 
    201                         # Save any uploaded files.
    202                         for f in related.opts.fields:
    203                             if child_follow.get(f.name, None):
    204                                 if isinstance(f, FileField) and rel_new_data.get(f.name, False):
    205                                     f.save_file(rel_new_data, new_rel_obj, self.change and old_rel_obj or None, old_rel_obj is not None, rel=True)
    206 
    207                         # Calculate whether any fields have changed.
    208                         if self.change:
    209                             if not old_rel_obj: # This object didn't exist before.
    210                                 self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj))
    211                             else:
    212                                 for f in related.opts.fields:
    213                                     if not f.primary_key and f != related.field and smart_str(getattr(old_rel_obj, f.attname)) != smart_str(getattr(new_rel_obj, f.attname)):
    214                                         self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
    215 
    216                         # Save many-to-many objects.
    217                         for f in related.opts.many_to_many:
    218                             if child_follow.get(f.name, None) and not f.rel.edit_inline:
    219                                 new_value = rel_new_data[f.attname]
    220                                 setattr(new_rel_obj, f.name, f.rel.to.objects.filter(pk__in=new_value))
    221                                 if self.change:
    222                                     self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
    223 
    224                     # If, in the change stage, all of the core fields were blank and
    225                     # the primary key (ID) was provided, delete the item.
    226                     if self.change and all_cores_blank and old_rel_obj:
    227                         new_rel_obj.delete()
    228                         self.fields_deleted.append('%s "%s"' % (related.opts.verbose_name, old_rel_obj))
    229 
    230         # Save the order, if applicable.
    231         if self.change and self.opts.get_ordered_objects():
    232             order = new_data['order_'] and map(int, new_data['order_'].split(',')) or []
    233             for rel_opts in self.opts.get_ordered_objects():
    234                 getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
    235         return new_object
    236 
    237     def get_related_objects(self):
    238         return self.opts.get_followed_related_objects(self.follow)
    239 
    240     def flatten_data(self):
    241         new_data = {}
    242         obj = self.change and self.original_object or None
    243         for f in self.opts.get_data_holders(self.follow):
    244             fol = self.follow.get(f.name)
    245             new_data.update(f.flatten_data(fol, obj))
    246         return new_data
    247 
    248 class AutomaticAddManipulator(AutomaticManipulator):
    249     change = False
    250 
    251 class AutomaticChangeManipulator(AutomaticManipulator):
    252     change = True
    253     def __init__(self, obj_key, follow=None):
    254         self.obj_key = obj_key
    255         try:
    256             self.original_object = self.manager.get(pk=obj_key)
    257         except ObjectDoesNotExist:
    258             # If the object doesn't exist, this might be a manipulator for a
    259             # one-to-one related object that hasn't created its subobject yet.
    260             # For example, this might be a Restaurant for a Place that doesn't
    261             # yet have restaurant information.
    262             if self.opts.one_to_one_field:
    263                 # Sanity check -- Make sure the "parent" object exists.
    264                 # For example, make sure the Place exists for the Restaurant.
    265                 # Let the ObjectDoesNotExist exception propagate up.
    266                 limit_choices_to = self.opts.one_to_one_field.rel.limit_choices_to
    267                 lookup_kwargs = {'%s__exact' % self.opts.one_to_one_field.rel.field_name: obj_key}
    268                 self.opts.one_to_one_field.rel.to.get_model_module().complex_filter(limit_choices_to).get(**lookup_kwargs)
    269                 params = dict([(f.attname, f.get_default()) for f in self.opts.fields])
    270                 params[self.opts.pk.attname] = obj_key
    271                 self.original_object = self.opts.get_model_module().Klass(**params)
    272             else:
    273                 raise
    274         super(AutomaticChangeManipulator, self).__init__(follow=follow)
    275 
    276 def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
    277     from django.db.models.fields.related import ManyToOneRel
    278     from django.utils.text import get_text_list
    279     field_list = [opts.get_field(field_name) for field_name in field_name_list]
    280     if isinstance(field_list[0].rel, ManyToOneRel):
    281         kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data}
    282     else:
    283         kwargs = {'%s__iexact' % field_name_list[0]: field_data}
    284     for f in field_list[1:]:
    285         # This is really not going to work for fields that have different
    286         # form fields, e.g. DateTime.
    287         # This validation needs to occur after html2python to be effective.
    288         field_val = all_data.get(f.name, None)
    289         if field_val is None:
    290             # This will be caught by another validator, assuming the field
    291             # doesn't have blank=True.
    292             return
    293         if isinstance(f.rel, ManyToOneRel):
    294             kwargs['%s__pk' % f.name] = field_val
    295         else:
    296             kwargs['%s__iexact' % f.name] = field_val
    297     try:
    298         old_obj = self.manager.get(**kwargs)
    299     except ObjectDoesNotExist:
    300         return
    301     if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val():
    302         pass
    303     else:
    304         raise validators.ValidationError, _("%(object)s with this %(type)s already exists for the given %(field)s.") % \
    305             {'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list([f.verbose_name for f in field_list[1:]], _('and'))}
    306 
    307 def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
    308     from django.db.models.fields.related import ManyToOneRel
    309     date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
    310     date_val = oldforms.DateField.html2python(date_str)
    311     if date_val is None:
    312         return # Date was invalid. This will be caught by another validator.
    313     lookup_kwargs = {'%s__year' % date_field.name: date_val.year}
    314     if isinstance(from_field.rel, ManyToOneRel):
    315         lookup_kwargs['%s__pk' % from_field.name] = field_data
    316     else:
    317         lookup_kwargs['%s__iexact' % from_field.name] = field_data
    318     if lookup_type in ('month', 'date'):
    319         lookup_kwargs['%s__month' % date_field.name] = date_val.month
    320     if lookup_type == 'date':
    321         lookup_kwargs['%s__day' % date_field.name] = date_val.day
    322     try:
    323         old_obj = self.manager.get(**lookup_kwargs)
    324     except ObjectDoesNotExist:
    325         return
    326     else:
    327         if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val():
    328             pass
    329         else:
    330             format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y'
    331             date_val = datetime_safe.new_datetime(date_val)
    332             raise validators.ValidationError, "Please enter a different %s. The one you entered is already being used for %s." % \
    333                 (from_field.verbose_name, date_val.strftime(format_string))
  • django/db/models/options.py

    diff --git a/django/db/models/options.py b/django/db/models/options.py
    index ffea6d5..3648cf0 100644
    a b class Options(object):  
    396396            self._related_many_to_many_cache = cache
    397397        return cache
    398398
    399     def get_followed_related_objects(self, follow=None):
    400         if follow == None:
    401             follow = self.get_follow()
    402         return [f for f in self.get_all_related_objects() if follow.get(f.name, None)]
    403 
    404     def get_data_holders(self, follow=None):
    405         if follow == None:
    406             follow = self.get_follow()
    407         return [f for f in self.fields + self.many_to_many + self.get_all_related_objects() if follow.get(f.name, None)]
    408 
    409     def get_follow(self, override=None):
    410         follow = {}
    411         for f in self.fields + self.many_to_many + self.get_all_related_objects():
    412             if override and f.name in override:
    413                 child_override = override[f.name]
    414             else:
    415                 child_override = None
    416             fol = f.get_follow(child_override)
    417             if fol != None:
    418                 follow[f.name] = fol
    419         return follow
    420 
    421399    def get_base_chain(self, model):
    422400        """
    423401        Returns a list of parent classes leading to 'model' (order from closet
    class Options(object):  
    459437            #        objects.append(opts)
    460438            self._ordered_objects = objects
    461439        return self._ordered_objects
    462 
    463     def has_field_type(self, field_type, follow=None):
    464         """
    465         Returns True if this object's admin form has at least one of the given
    466         field_type (e.g. FileField).
    467         """
    468         # TODO: follow
    469         if not hasattr(self, '_field_types'):
    470             self._field_types = {}
    471         if field_type not in self._field_types:
    472             try:
    473                 # First check self.fields.
    474                 for f in self.fields:
    475                     if isinstance(f, field_type):
    476                         raise StopIteration
    477                 # Failing that, check related fields.
    478                 for related in self.get_followed_related_objects(follow):
    479                     for f in related.opts.fields:
    480                         if isinstance(f, field_type):
    481                             raise StopIteration
    482             except StopIteration:
    483                 self._field_types[field_type] = True
    484             else:
    485                 self._field_types[field_type] = False
    486         return self._field_types[field_type]
  • django/db/models/related.py

    diff --git a/django/db/models/related.py b/django/db/models/related.py
    index 2c1dc5c..ff7c787 100644
    a b class RelatedObject(object):  
    1515        self.model = model
    1616        self.opts = model._meta
    1717        self.field = field
    18         self.edit_inline = field.rel.edit_inline
    1918        self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name)
    2019        self.var_name = self.opts.object_name.lower()
    2120
    22     def flatten_data(self, follow, obj=None):
    23         new_data = {}
    24         rel_instances = self.get_list(obj)
    25         for i, rel_instance in enumerate(rel_instances):
    26             instance_data = {}
    27             for f in self.opts.fields + self.opts.many_to_many:
    28                 # TODO: Fix for recursive manipulators.
    29                 fol = follow.get(f.name, None)
    30                 if fol:
    31                     field_data = f.flatten_data(fol, rel_instance)
    32                     for name, value in field_data.items():
    33                         instance_data['%s.%d.%s' % (self.var_name, i, name)] = value
    34             new_data.update(instance_data)
    35         return new_data
    36 
    37     def extract_data(self, data):
    38         """
    39         Pull out the data meant for inline objects of this class,
    40         i.e. anything starting with our module name.
    41         """
    42         return data # TODO
    43 
    44     def get_list(self, parent_instance=None):
    45         "Get the list of this type of object from an instance of the parent class."
    46         if parent_instance is not None:
    47             attr = getattr(parent_instance, self.get_accessor_name())
    48             if self.field.rel.multiple:
    49                 # For many-to-many relationships, return a list of objects
    50                 # corresponding to the xxx_num_in_admin options of the field
    51                 objects = list(attr.all())
    52 
    53                 count = len(objects) + self.field.rel.num_extra_on_change
    54                 if self.field.rel.min_num_in_admin:
    55                     count = max(count, self.field.rel.min_num_in_admin)
    56                 if self.field.rel.max_num_in_admin:
    57                     count = min(count, self.field.rel.max_num_in_admin)
    58 
    59                 change = count - len(objects)
    60                 if change > 0:
    61                     return objects + [None] * change
    62                 if change < 0:
    63                     return objects[:change]
    64                 else: # Just right
    65                     return objects
    66             else:
    67                 # A one-to-one relationship, so just return the single related
    68                 # object
    69                 return [attr]
    70         else:
    71             if self.field.rel.min_num_in_admin:
    72                 return [None] * max(self.field.rel.num_in_admin, self.field.rel.min_num_in_admin)
    73             else:
    74                 return [None] * self.field.rel.num_in_admin
    75 
    7621    def get_db_prep_lookup(self, lookup_type, value):
    7722        # Defer to the actual field definition for db prep
    7823        return self.field.get_db_prep_lookup(lookup_type, value)
    79        
     24
    8025    def editable_fields(self):
    8126        "Get the fields in this class that should be edited inline."
    8227        return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
    8328
    84     def get_follow(self, override=None):
    85         if isinstance(override, bool):
    86             if override:
    87                 over = {}
    88             else:
    89                 return None
    90         else:
    91             if override:
    92                 over = override.copy()
    93             elif self.edit_inline:
    94                 over = {}
    95             else:
    96                 return None
    97 
    98         over[self.field.name] = False
    99         return self.opts.get_follow(over)
    100 
    101     def get_manipulator_fields(self, opts, manipulator, change, follow):
    102         if self.field.rel.multiple:
    103             if change:
    104                 attr = getattr(manipulator.original_object, self.get_accessor_name())
    105                 count = attr.count()
    106                 count += self.field.rel.num_extra_on_change
    107             else:
    108                 count = self.field.rel.num_in_admin
    109             if self.field.rel.min_num_in_admin:
    110                 count = max(count, self.field.rel.min_num_in_admin)
    111             if self.field.rel.max_num_in_admin:
    112                 count = min(count, self.field.rel.max_num_in_admin)
    113         else:
    114             count = 1
    115 
    116         fields = []
    117         for i in range(count):
    118             for f in self.opts.fields + self.opts.many_to_many:
    119                 if follow.get(f.name, False):
    120                     prefix = '%s.%d.' % (self.var_name, i)
    121                     fields.extend(f.get_manipulator_fields(self.opts, manipulator, change,
    122                                                            name_prefix=prefix, rel=True))
    123         return fields
    124 
    12529    def __repr__(self):
    12630        return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
    12731
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index ee9b8c6..afbf614 100644
    a b __all__ = (  
    3838    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
    3939    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
    4040    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    41     'SplitDateTimeField', 'IPAddressField', 'FilePathField',
     41    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
    4242)
    4343
    4444# These values, if given to to_python(), will trigger the self.required check.
    class IPAddressField(RegexField):  
    835835
    836836    def __init__(self, *args, **kwargs):
    837837        super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs)
     838
     839slug_re = re.compile(r'^[-\w]+$')
     840
     841class SlugField(RegexField):
     842    default_error_messages = {
     843        'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers,"
     844                     u" underscores or hyphens."),
     845    }
     846
     847    def __init__(self, *args, **kwargs):
     848        super(SlugField, self).__init__(slug_re, *args, **kwargs)
  • deleted file django/oldforms/__init__.py

    diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py
    deleted file mode 100644
    index b5698ab..0000000
    + -  
    1 from django.core import validators
    2 from django.core.exceptions import PermissionDenied
    3 from django.utils.html import escape
    4 from django.utils.safestring import mark_safe
    5 from django.conf import settings
    6 from django.utils.translation import ugettext, ungettext
    7 from django.utils.encoding import smart_unicode, force_unicode
    8 
    9 FORM_FIELD_ID_PREFIX = 'id_'
    10 
    11 class EmptyValue(Exception):
    12     "This is raised when empty data is provided"
    13     pass
    14 
    15 class Manipulator(object):
    16     # List of permission strings. User must have at least one to manipulate.
    17     # None means everybody has permission.
    18     required_permission = ''
    19 
    20     def __init__(self):
    21         # List of FormField objects
    22         self.fields = []
    23 
    24     def __getitem__(self, field_name):
    25         "Looks up field by field name; raises KeyError on failure"
    26         for field in self.fields:
    27             if field.field_name == field_name:
    28                 return field
    29         raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields))
    30 
    31     def __delitem__(self, field_name):
    32         "Deletes the field with the given field name; raises KeyError on failure"
    33         for i, field in enumerate(self.fields):
    34             if field.field_name == field_name:
    35                 del self.fields[i]
    36                 return
    37         raise KeyError, "Field %s not found" % field_name
    38 
    39     def check_permissions(self, user):
    40         """Confirms user has required permissions to use this manipulator; raises
    41         PermissionDenied on failure."""
    42         if self.required_permission is None:
    43             return
    44         if user.has_perm(self.required_permission):
    45             return
    46         raise PermissionDenied
    47 
    48     def prepare(self, new_data):
    49         """
    50         Makes any necessary preparations to new_data, in place, before data has
    51         been validated.
    52         """
    53         for field in self.fields:
    54             field.prepare(new_data)
    55 
    56     def get_validation_errors(self, new_data):
    57         "Returns dictionary mapping field_names to error-message lists"
    58         errors = {}
    59         self.prepare(new_data)
    60         for field in self.fields:
    61             errors.update(field.get_validation_errors(new_data))
    62             val_name = 'validate_%s' % field.field_name
    63             if hasattr(self, val_name):
    64                 val = getattr(self, val_name)
    65                 try:
    66                     field.run_validator(new_data, val)
    67                 except (validators.ValidationError, validators.CriticalValidationError), e:
    68                     errors.setdefault(field.field_name, []).extend(e.messages)
    69 
    70 #            if field.is_required and not new_data.get(field.field_name, False):
    71 #                errors.setdefault(field.field_name, []).append(ugettext_lazy('This field is required.'))
    72 #                continue
    73 #            try:
    74 #                validator_list = field.validator_list
    75 #                if hasattr(self, 'validate_%s' % field.field_name):
    76 #                    validator_list.append(getattr(self, 'validate_%s' % field.field_name))
    77 #                for validator in validator_list:
    78 #                    if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'):
    79 #                        try:
    80 #                            if hasattr(field, 'requires_data_list'):
    81 #                                validator(new_data.getlist(field.field_name), new_data)
    82 #                            else:
    83 #                                validator(new_data.get(field.field_name, ''), new_data)
    84 #                        except validators.ValidationError, e:
    85 #                            errors.setdefault(field.field_name, []).extend(e.messages)
    86 #            # If a CriticalValidationError is raised, ignore any other ValidationErrors
    87 #            # for this particular field
    88 #            except validators.CriticalValidationError, e:
    89 #                errors.setdefault(field.field_name, []).extend(e.messages)
    90         return errors
    91 
    92     def save(self, new_data):
    93         "Saves the changes and returns the new object"
    94         # changes is a dictionary-like object keyed by field_name
    95         raise NotImplementedError
    96 
    97     def do_html2python(self, new_data):
    98         """
    99         Convert the data from HTML data types to Python datatypes, changing the
    100         object in place. This happens after validation but before storage. This
    101         must happen after validation because html2python functions aren't
    102         expected to deal with invalid input.
    103         """
    104         for field in self.fields:
    105             field.convert_post_data(new_data)
    106 
    107 class FormWrapper(object):
    108     """
    109     A wrapper linking a Manipulator to the template system.
    110     This allows dictionary-style lookups of formfields. It also handles feeding
    111     prepopulated data and validation error messages to the formfield objects.
    112     """
    113     def __init__(self, manipulator, data=None, error_dict=None, edit_inline=True):
    114         self.manipulator = manipulator
    115         if data is None:
    116             data = {}
    117         if error_dict is None:
    118             error_dict = {}
    119         self.data = data
    120         self.error_dict = error_dict
    121         self._inline_collections = None
    122         self.edit_inline = edit_inline
    123 
    124     def __repr__(self):
    125         return repr(self.__dict__)
    126 
    127     def __getitem__(self, key):
    128         for field in self.manipulator.fields:
    129             if field.field_name == key:
    130                 data = field.extract_data(self.data)
    131                 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, []))
    132         if self.edit_inline:
    133             self.fill_inline_collections()
    134             for inline_collection in self._inline_collections:
    135                 # The 'orig_name' comparison is for backwards compatibility
    136                 # with hand-crafted forms.
    137                 if inline_collection.name == key or (':' not in key and inline_collection.orig_name == key):
    138                     return inline_collection
    139         raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key
    140 
    141     def fill_inline_collections(self):
    142         if not self._inline_collections:
    143             ic = []
    144             related_objects = self.manipulator.get_related_objects()
    145             for rel_obj in related_objects:
    146                 data = rel_obj.extract_data(self.data)
    147                 inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict)
    148                 ic.append(inline_collection)
    149             self._inline_collections = ic
    150 
    151     def has_errors(self):
    152         return self.error_dict != {}
    153 
    154     def _get_fields(self):
    155         try:
    156             return self._fields
    157         except AttributeError:
    158             self._fields = [self.__getitem__(field.field_name) for field in self.manipulator.fields]
    159             return self._fields
    160 
    161     fields = property(_get_fields)
    162 
    163 class FormFieldWrapper(object):
    164     "A bridge between the template system and an individual form field. Used by FormWrapper."
    165     def __init__(self, formfield, data, error_list):
    166         self.formfield, self.data, self.error_list = formfield, data, error_list
    167         self.field_name = self.formfield.field_name # for convenience in templates
    168 
    169     def __str__(self):
    170         "Renders the field"
    171         return unicode(self).encode('utf-8')
    172 
    173     def __unicode__(self):
    174         "Renders the field"
    175         return force_unicode(self.formfield.render(self.data))
    176 
    177     def __repr__(self):
    178         return '<FormFieldWrapper for "%s">' % self.formfield.field_name
    179 
    180     def field_list(self):
    181         """
    182         Like __str__(), but returns a list. Use this when the field's render()
    183         method returns a list.
    184         """
    185         return self.formfield.render(self.data)
    186 
    187     def errors(self):
    188         return self.error_list
    189 
    190     def html_error_list(self):
    191         if self.errors():
    192             return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]))
    193         else:
    194             return mark_safe('')
    195 
    196     def get_id(self):
    197         return self.formfield.get_id()
    198 
    199 class FormFieldCollection(FormFieldWrapper):
    200     "A utility class that gives the template access to a dict of FormFieldWrappers"
    201     def __init__(self, formfield_dict):
    202         self.formfield_dict = formfield_dict
    203 
    204     def __str__(self):
    205         return unicode(self).encode('utf-8')
    206 
    207     def __unicode__(self):
    208         return unicode(self.formfield_dict)
    209 
    210     def __getitem__(self, template_key):
    211         "Look up field by template key; raise KeyError on failure"
    212         return self.formfield_dict[template_key]
    213 
    214     def __repr__(self):
    215         return "<FormFieldCollection: %s>" % self.formfield_dict
    216 
    217     def errors(self):
    218         "Returns list of all errors in this collection's formfields"
    219         errors = []
    220         for field in self.formfield_dict.values():
    221             if hasattr(field, 'errors'):
    222                 errors.extend(field.errors())
    223         return errors
    224 
    225     def has_errors(self):
    226         return bool(len(self.errors()))
    227 
    228     def html_combined_error_list(self):
    229         return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]))
    230 
    231 class InlineObjectCollection(object):
    232     "An object that acts like a sparse list of form field collections."
    233     def __init__(self, parent_manipulator, rel_obj, data, errors):
    234         self.parent_manipulator = parent_manipulator
    235         self.rel_obj = rel_obj
    236         self.data = data
    237         self.errors = errors
    238         self._collections = None
    239         self.name = rel_obj.name
    240         # This is the name used prior to fixing #1839. Needs for backwards
    241         # compatibility.
    242         self.orig_name = rel_obj.opts.module_name
    243 
    244     def __len__(self):
    245         self.fill()
    246         return self._collections.__len__()
    247 
    248     def __getitem__(self, k):
    249         self.fill()
    250         return self._collections.__getitem__(k)
    251 
    252     def __setitem__(self, k, v):
    253         self.fill()
    254         return self._collections.__setitem__(k,v)
    255 
    256     def __delitem__(self, k):
    257         self.fill()
    258         return self._collections.__delitem__(k)
    259 
    260     def __iter__(self):
    261         self.fill()
    262         return iter(self._collections.values())
    263 
    264     def items(self):
    265         self.fill()
    266         return self._collections.items()
    267 
    268     def fill(self):
    269         if self._collections:
    270             return
    271         else:
    272             var_name = self.rel_obj.opts.object_name.lower()
    273             collections = {}
    274             orig = None
    275             if hasattr(self.parent_manipulator, 'original_object'):
    276                 orig = self.parent_manipulator.original_object
    277             orig_list = self.rel_obj.get_list(orig)
    278 
    279             for i, instance in enumerate(orig_list):
    280                 collection = {'original': instance}
    281                 for f in self.rel_obj.editable_fields():
    282                     for field_name in f.get_manipulator_field_names(''):
    283                         full_field_name = '%s.%d.%s' % (var_name, i, field_name)
    284                         field = self.parent_manipulator[full_field_name]
    285                         data = field.extract_data(self.data)
    286                         errors = self.errors.get(full_field_name, [])
    287                         collection[field_name] = FormFieldWrapper(field, data, errors)
    288                 collections[i] = FormFieldCollection(collection)
    289             self._collections = collections
    290 
    291 
    292 class FormField(object):
    293     """Abstract class representing a form field.
    294 
    295     Classes that extend FormField should define the following attributes:
    296         field_name
    297             The field's name for use by programs.
    298         validator_list
    299             A list of validation tests (callback functions) that the data for
    300             this field must pass in order to be added or changed.
    301         is_required
    302             A Boolean. Is it a required field?
    303     Subclasses should also implement a render(data) method, which is responsible
    304     for rending the form field in XHTML.
    305     """
    306 
    307     def __str__(self):
    308         return unicode(self).encode('utf-8')
    309 
    310     def __unicode__(self):
    311         return self.render(u'')
    312 
    313     def __repr__(self):
    314         return 'FormField "%s"' % self.field_name
    315 
    316     def prepare(self, new_data):
    317         "Hook for doing something to new_data (in place) before validation."
    318         pass
    319 
    320     def html2python(data):
    321         "Hook for converting an HTML datatype (e.g. 'on' for checkboxes) to a Python type"
    322         return data
    323     html2python = staticmethod(html2python)
    324 
    325     def render(self, data):
    326         raise NotImplementedError
    327 
    328     def get_member_name(self):
    329         if hasattr(self, 'member_name'):
    330             return self.member_name
    331         else:
    332             return self.field_name
    333 
    334     def extract_data(self, data_dict):
    335         if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'):
    336             data = data_dict.getlist(self.get_member_name())
    337         else:
    338             data = data_dict.get(self.get_member_name(), None)
    339         if data is None:
    340             data = ''
    341         return data
    342 
    343     def convert_post_data(self, new_data):
    344         name = self.get_member_name()
    345         if self.field_name in new_data:
    346             d = new_data.getlist(self.field_name)
    347             try:
    348                 converted_data = [self.__class__.html2python(data) for data in d]
    349             except ValueError:
    350                 converted_data = d
    351             new_data.setlist(name, converted_data)
    352         else:
    353             try:
    354                 #individual fields deal with None values themselves
    355                 new_data.setlist(name, [self.__class__.html2python(None)])
    356             except EmptyValue:
    357                 new_data.setlist(name, [])
    358 
    359 
    360     def run_validator(self, new_data, validator):
    361         if self.is_required or new_data.get(self.field_name, False) or hasattr(validator, 'always_test'):
    362             if hasattr(self, 'requires_data_list'):
    363                 validator(new_data.getlist(self.field_name), new_data)
    364             else:
    365                 validator(new_data.get(self.field_name, ''), new_data)
    366 
    367     def get_validation_errors(self, new_data):
    368         errors = {}
    369         if self.is_required and not new_data.get(self.field_name, False):
    370             errors.setdefault(self.field_name, []).append(ugettext('This field is required.'))
    371             return errors
    372         try:
    373             for validator in self.validator_list:
    374                 try:
    375                     self.run_validator(new_data, validator)
    376                 except validators.ValidationError, e:
    377                     errors.setdefault(self.field_name, []).extend(e.messages)
    378         # If a CriticalValidationError is raised, ignore any other ValidationErrors
    379         # for this particular field
    380         except validators.CriticalValidationError, e:
    381             errors.setdefault(self.field_name, []).extend(e.messages)
    382         return errors
    383 
    384     def get_id(self):
    385         "Returns the HTML 'id' attribute for this form field."
    386         return FORM_FIELD_ID_PREFIX + self.field_name
    387 
    388 ####################
    389 # GENERIC WIDGETS  #
    390 ####################
    391 
    392 class TextField(FormField):
    393     input_type = "text"
    394     def __init__(self, field_name, length=30, max_length=None, is_required=False, validator_list=None, member_name=None):
    395         if validator_list is None: validator_list = []
    396         self.field_name = field_name
    397         self.length, self.max_length = length, max_length
    398         self.is_required = is_required
    399         self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list
    400         if member_name != None:
    401             self.member_name = member_name
    402 
    403     def isValidLength(self, data, form):
    404         if data and self.max_length and len(smart_unicode(data)) > self.max_length:
    405             raise validators.ValidationError, ungettext("Ensure your text is less than %s character.",
    406                 "Ensure your text is less than %s characters.", self.max_length) % self.max_length
    407 
    408     def hasNoNewlines(self, data, form):
    409         if data and '\n' in data:
    410             raise validators.ValidationError, ugettext("Line breaks are not allowed here.")
    411 
    412     def render(self, data):
    413         if data is None:
    414             data = u''
    415         max_length = u''
    416         if self.max_length:
    417             max_length = u'maxlength="%s" ' % self.max_length
    418         return mark_safe(u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
    419             (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and u' required' or '',
    420             self.field_name, self.length, escape(data), max_length))
    421 
    422     def html2python(data):
    423         return data
    424     html2python = staticmethod(html2python)
    425 
    426 class PasswordField(TextField):
    427     input_type = "password"
    428 
    429 class LargeTextField(TextField):
    430     def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, max_length=None):
    431         if validator_list is None: validator_list = []
    432         self.field_name = field_name
    433         self.rows, self.cols, self.is_required = rows, cols, is_required
    434         self.validator_list = validator_list[:]
    435         if max_length:
    436             self.validator_list.append(self.isValidLength)
    437             self.max_length = max_length
    438 
    439     def render(self, data):
    440         if data is None:
    441             data = ''
    442         return mark_safe(u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
    443             (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'',
    444             self.field_name, self.rows, self.cols, escape(data)))
    445 
    446 class HiddenField(FormField):
    447     def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
    448         if validator_list is None: validator_list = []
    449         self.field_name, self.is_required = field_name, is_required
    450         self.validator_list = validator_list[:]
    451 
    452     def render(self, data):
    453         return mark_safe(u'<input type="hidden" id="%s" name="%s" value="%s" />' % \
    454             (self.get_id(), self.field_name, escape(data)))
    455 
    456 class CheckboxField(FormField):
    457     def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
    458         if validator_list is None: validator_list = []
    459         self.field_name = field_name
    460         self.checked_by_default = checked_by_default
    461         self.is_required = is_required
    462         self.validator_list = validator_list[:]
    463 
    464     def render(self, data):
    465         checked_html = ''
    466         if data or (data is '' and self.checked_by_default):
    467             checked_html = ' checked="checked"'
    468         return mark_safe(u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
    469             (self.get_id(), self.__class__.__name__,
    470             self.field_name, checked_html))
    471 
    472     def html2python(data):
    473         "Convert value from browser ('on' or '') to a Python boolean"
    474         if data == 'on':
    475             return True
    476         return False
    477     html2python = staticmethod(html2python)
    478 
    479 class SelectField(FormField):
    480     def __init__(self, field_name, choices=None, size=1, is_required=False, validator_list=None, member_name=None):
    481         if validator_list is None: validator_list = []
    482         if choices is None: choices = []
    483         choices = [(k, smart_unicode(v, strings_only=True)) for k, v in choices]
    484         self.field_name = field_name
    485         # choices is a list of (value, human-readable key) tuples because order matters
    486         self.choices, self.size, self.is_required = choices, size, is_required
    487         self.validator_list = [self.isValidChoice] + validator_list
    488         if member_name != None:
    489             self.member_name = member_name
    490 
    491     def render(self, data):
    492         output = [u'<select id="%s" class="v%s%s" name="%s" size="%s">' % \
    493             (self.get_id(), self.__class__.__name__,
    494              self.is_required and u' required' or u'', self.field_name, self.size)]
    495         str_data = smart_unicode(data) # normalize to string
    496         for value, display_name in self.choices:
    497             selected_html = u''
    498             if smart_unicode(value) == str_data:
    499                 selected_html = u' selected="selected"'
    500             output.append(u'    <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(display_name))))
    501         output.append(u'  </select>')
    502         return mark_safe(u'\n'.join(output))
    503 
    504     def isValidChoice(self, data, form):
    505         str_data = smart_unicode(data)
    506         str_choices = [smart_unicode(item[0]) for item in self.choices]
    507         if str_data not in str_choices:
    508             raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data': str_data, 'choices': str_choices}
    509 
    510 class NullSelectField(SelectField):
    511     "This SelectField converts blank fields to None"
    512     def html2python(data):
    513         if not data:
    514             return None
    515         return data
    516     html2python = staticmethod(html2python)
    517 
    518 class RadioSelectField(FormField):
    519     def __init__(self, field_name, choices=None, ul_class='', is_required=False, validator_list=None, member_name=None):
    520         if validator_list is None: validator_list = []
    521         if choices is None: choices = []
    522         choices = [(k, smart_unicode(v)) for k, v in choices]
    523         self.field_name = field_name
    524         # choices is a list of (value, human-readable key) tuples because order matters
    525         self.choices, self.is_required = choices, is_required
    526         self.validator_list = [self.isValidChoice] + validator_list
    527         self.ul_class = ul_class
    528         if member_name != None:
    529             self.member_name = member_name
    530 
    531     def render(self, data):
    532         """
    533         Returns a special object, RadioFieldRenderer, that is iterable *and*
    534         has a default unicode() rendered output.
    535 
    536         This allows for flexible use in templates. You can just use the default
    537         rendering:
    538 
    539             {{ field_name }}
    540 
    541         ...which will output the radio buttons in an unordered list.
    542         Or, you can manually traverse each radio option for special layout:
    543 
    544             {% for option in field_name.field_list %}
    545                 {{ option.field }} {{ option.label }}<br />
    546             {% endfor %}
    547         """
    548         class RadioFieldRenderer:
    549             def __init__(self, datalist, ul_class):
    550                 self.datalist, self.ul_class = datalist, ul_class
    551             def __unicode__(self):
    552                 "Default unicode() output for this radio field -- a <ul>"
    553                 output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')]
    554                 output.extend([u'<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
    555                 output.append(u'</ul>')
    556                 return mark_safe(u''.join(output))
    557             def __iter__(self):
    558                 for d in self.datalist:
    559                     yield d
    560             def __len__(self):
    561                 return len(self.datalist)
    562         datalist = []
    563         str_data = smart_unicode(data) # normalize to string
    564         for i, (value, display_name) in enumerate(self.choices):
    565             selected_html = ''
    566             if smart_unicode(value) == str_data:
    567                 selected_html = u' checked="checked"'
    568             datalist.append({
    569                 'value': value,
    570                 'name': display_name,
    571                 'field': mark_safe(u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
    572                     (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html)),
    573                 'label': mark_safe(u'<label for="%s">%s</label>' % \
    574                     (self.get_id() + u'_' + unicode(i), display_name),
    575             )})
    576         return RadioFieldRenderer(datalist, self.ul_class)
    577 
    578     def isValidChoice(self, data, form):
    579         str_data = smart_unicode(data)
    580         str_choices = [smart_unicode(item[0]) for item in self.choices]
    581         if str_data not in str_choices:
    582             raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':str_data, 'choices':str_choices}
    583 
    584 class NullBooleanField(SelectField):
    585     "This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
    586     def __init__(self, field_name, is_required=False, validator_list=None):
    587         if validator_list is None: validator_list = []
    588         SelectField.__init__(self, field_name, choices=[('1', ugettext('Unknown')), ('2', ugettext('Yes')), ('3', ugettext('No'))],
    589             is_required=is_required, validator_list=validator_list)
    590 
    591     def render(self, data):
    592         if data is None: data = '1'
    593         elif data == True: data = '2'
    594         elif data == False: data = '3'
    595         return SelectField.render(self, data)
    596 
    597     def html2python(data):
    598         return {None: None, '1': None, '2': True, '3': False}[data]
    599     html2python = staticmethod(html2python)
    600 
    601 class SelectMultipleField(SelectField):
    602     requires_data_list = True
    603     def render(self, data):
    604         output = [u'<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \
    605             (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'',
    606             self.field_name, self.size)]
    607         str_data_list = map(smart_unicode, data) # normalize to strings
    608         for value, choice in self.choices:
    609             selected_html = u''
    610             if smart_unicode(value) in str_data_list:
    611                 selected_html = u' selected="selected"'
    612             output.append(u'    <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(choice))))
    613         output.append(u'  </select>')
    614         return mark_safe(u'\n'.join(output))
    615 
    616     def isValidChoice(self, field_data, all_data):
    617         # data is something like ['1', '2', '3']
    618         str_choices = [smart_unicode(item[0]) for item in self.choices]
    619         for val in map(smart_unicode, field_data):
    620             if val not in str_choices:
    621                 raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':val, 'choices':str_choices}
    622 
    623     def html2python(data):
    624         if data is None:
    625             raise EmptyValue
    626         return data
    627     html2python = staticmethod(html2python)
    628 
    629 class CheckboxSelectMultipleField(SelectMultipleField):
    630     """
    631     This has an identical interface to SelectMultipleField, except the rendered
    632     widget is different. Instead of a <select multiple>, this widget outputs a
    633     <ul> of <input type="checkbox">es.
    634 
    635     Of course, that results in multiple form elements for the same "single"
    636     field, so this class's prepare() method flattens the split data elements
    637     back into the single list that validators, renderers and save() expect.
    638     """
    639     requires_data_list = True
    640     def __init__(self, field_name, choices=None, ul_class='', validator_list=None):
    641         if validator_list is None: validator_list = []
    642         if choices is None: choices = []
    643         self.ul_class = ul_class
    644         SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
    645 
    646     def prepare(self, new_data):
    647         # new_data has "split" this field into several fields, so flatten it
    648         # back into a single list.
    649         data_list = []
    650         for value, readable_value in self.choices:
    651             if new_data.get('%s%s' % (self.field_name, value), '') == 'on':
    652                 data_list.append(value)
    653         new_data.setlist(self.field_name, data_list)
    654 
    655     def render(self, data):
    656         output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')]
    657         str_data_list = map(smart_unicode, data) # normalize to strings
    658         for value, choice in self.choices:
    659             checked_html = u''
    660             if smart_unicode(value) in str_data_list:
    661                 checked_html = u' checked="checked"'
    662             field_name = u'%s%s' % (self.field_name, value)
    663             output.append(u'<li><input type="checkbox" id="%s" class="v%s" name="%s"%s value="on" /> <label for="%s">%s</label></li>' % \
    664                 (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
    665                 self.get_id() + escape(value), choice))
    666         output.append(u'</ul>')
    667         return mark_safe(u'\n'.join(output))
    668 
    669 ####################
    670 # FILE UPLOADS     #
    671 ####################
    672 
    673 class FileUploadField(FormField):
    674     def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
    675         if validator_list is None: validator_list = []
    676         self.field_name, self.is_required = field_name, is_required
    677         self.validator_list = [self.isNonEmptyFile] + validator_list
    678 
    679     def isNonEmptyFile(self, new_data, all_data):
    680         if hasattr(new_data, 'upload_errors'):
    681             upload_errors = new_data.upload_errors()
    682             if upload_errors:
    683                 raise validators.CriticalValidationError, upload_errors
    684         try:
    685             file_size = new_data.size
    686         except AttributeError:
    687             file_size = len(new_data['content'])
    688         if not file_size:
    689             raise validators.CriticalValidationError, ugettext("The submitted file is empty.")
    690 
    691     def render(self, data):
    692         return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \
    693             (self.get_id(), self.__class__.__name__, self.field_name))
    694 
    695     def prepare(self, new_data):
    696         if hasattr(new_data, 'upload_errors'):
    697             upload_errors = new_data.upload_errors()
    698             new_data[self.field_name] = { '_file_upload_error': upload_errors }
    699 
    700     def html2python(data):
    701         if data is None:
    702             raise EmptyValue
    703         return data
    704     html2python = staticmethod(html2python)
    705 
    706 class ImageUploadField(FileUploadField):
    707     "A FileUploadField that raises CriticalValidationError if the uploaded file isn't an image."
    708     def __init__(self, *args, **kwargs):
    709         FileUploadField.__init__(self, *args, **kwargs)
    710         self.validator_list.insert(0, self.isValidImage)
    711 
    712     def isValidImage(self, field_data, all_data):
    713         try:
    714             validators.isValidImage(field_data, all_data)
    715         except validators.ValidationError, e:
    716             raise validators.CriticalValidationError, e.messages
    717 
    718 ####################
    719 # INTEGERS/FLOATS  #
    720 ####################
    721 
    722 class IntegerField(TextField):
    723     def __init__(self, field_name, length=10, max_length=None, is_required=False, validator_list=None, member_name=None):
    724         if validator_list is None: validator_list = []
    725         validator_list = [self.isInteger] + validator_list
    726         if member_name is not None:
    727             self.member_name = member_name
    728         TextField.__init__(self, field_name, length, max_length, is_required, validator_list)
    729 
    730     def isInteger(self, field_data, all_data):
    731         try:
    732             validators.isInteger(field_data, all_data)
    733         except validators.ValidationError, e:
    734             raise validators.CriticalValidationError, e.messages
    735 
    736     def html2python(data):
    737         if data == '' or data is None:
    738             return None
    739         return int(data)
    740     html2python = staticmethod(html2python)
    741 
    742 class SmallIntegerField(IntegerField):
    743     def __init__(self, field_name, length=5, max_length=5, is_required=False, validator_list=None):
    744         if validator_list is None: validator_list = []
    745         validator_list = [self.isSmallInteger] + validator_list
    746         IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
    747 
    748     def isSmallInteger(self, field_data, all_data):
    749         if not -32768 <= int(field_data) <= 32767:
    750             raise validators.CriticalValidationError, ugettext("Enter a whole number between -32,768 and 32,767.")
    751 
    752 class PositiveIntegerField(IntegerField):
    753     def __init__(self, field_name, length=10, max_length=None, is_required=False, validator_list=None):
    754         if validator_list is None: validator_list = []
    755         validator_list = [self.isPositive] + validator_list
    756         IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
    757 
    758     def isPositive(self, field_data, all_data):
    759         if int(field_data) < 0:
    760             raise validators.CriticalValidationError, ugettext("Enter a positive number.")
    761 
    762 class PositiveSmallIntegerField(IntegerField):
    763     def __init__(self, field_name, length=5, max_length=None, is_required=False, validator_list=None):
    764         if validator_list is None: validator_list = []
    765         validator_list = [self.isPositiveSmall] + validator_list
    766         IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
    767 
    768     def isPositiveSmall(self, field_data, all_data):
    769         if not 0 <= int(field_data) <= 32767:
    770             raise validators.CriticalValidationError, ugettext("Enter a whole number between 0 and 32,767.")
    771 
    772 class FloatField(TextField):
    773     def __init__(self, field_name, is_required=False, validator_list=None):
    774         if validator_list is None: validator_list = []
    775         validator_list = [validators.isValidFloat] + validator_list
    776         TextField.__init__(self, field_name, is_required=is_required, validator_list=validator_list)
    777 
    778     def html2python(data):
    779         if data == '' or data is None:
    780             return None
    781         return float(data)
    782     html2python = staticmethod(html2python)
    783 
    784 class DecimalField(TextField):
    785     def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None):
    786         if validator_list is None: validator_list = []
    787         self.max_digits, self.decimal_places = max_digits, decimal_places
    788         validator_list = [self.isValidDecimal] + validator_list
    789         # Initialise the TextField, making sure it's large enough to fit the number with a - sign and a decimal point.
    790         super(DecimalField, self).__init__(field_name, max_digits+2, max_digits+2, is_required, validator_list)
    791 
    792     def isValidDecimal(self, field_data, all_data):
    793         v = validators.IsValidDecimal(self.max_digits, self.decimal_places)
    794         try:
    795             v(field_data, all_data)
    796         except validators.ValidationError, e:
    797             raise validators.CriticalValidationError, e.messages
    798 
    799     def html2python(data):
    800         if data == '' or data is None:
    801             return None
    802         try:
    803             import decimal
    804         except ImportError:
    805             from django.utils import _decimal as decimal
    806         try:
    807             return decimal.Decimal(data)
    808         except decimal.InvalidOperation, e:
    809             raise ValueError, e
    810     html2python = staticmethod(html2python)
    811 
    812 ####################
    813 # DATES AND TIMES  #
    814 ####################
    815 
    816 class DatetimeField(TextField):
    817     """A FormField that automatically converts its data to a datetime.datetime object.
    818     The data should be in the format YYYY-MM-DD HH:MM:SS."""
    819     def __init__(self, field_name, length=30, max_length=None, is_required=False, validator_list=None):
    820         if validator_list is None: validator_list = []
    821         self.field_name = field_name
    822         self.length, self.max_length = length, max_length
    823         self.is_required = is_required
    824         self.validator_list = [validators.isValidANSIDatetime] + validator_list
    825 
    826     def html2python(data):
    827         "Converts the field into a datetime.datetime object"
    828         import datetime
    829         try:
    830             date, time = data.split()
    831             y, m, d = date.split('-')
    832             timebits = time.split(':')
    833             h, mn = timebits[:2]
    834             if len(timebits) > 2:
    835                 s = int(timebits[2])
    836             else:
    837                 s = 0
    838             return datetime.datetime(int(y), int(m), int(d), int(h), int(mn), s)
    839         except ValueError:
    840             return None
    841     html2python = staticmethod(html2python)
    842 
    843 class DateField(TextField):
    844     """A FormField that automatically converts its data to a datetime.date object.
    845     The data should be in the format YYYY-MM-DD."""
    846     def __init__(self, field_name, is_required=False, validator_list=None):
    847         if validator_list is None: validator_list = []
    848         validator_list = [self.isValidDate] + validator_list
    849         TextField.__init__(self, field_name, length=10, max_length=10,
    850             is_required=is_required, validator_list=validator_list)
    851 
    852     def isValidDate(self, field_data, all_data):
    853         try:
    854             validators.isValidANSIDate(field_data, all_data)
    855         except validators.ValidationError, e:
    856             raise validators.CriticalValidationError, e.messages
    857 
    858     def html2python(data):
    859         "Converts the field into a datetime.date object"
    860         import time, datetime
    861         try:
    862             time_tuple = time.strptime(data, '%Y-%m-%d')
    863             return datetime.date(*time_tuple[0:3])
    864         except (ValueError, TypeError):
    865             return None
    866     html2python = staticmethod(html2python)
    867 
    868 class TimeField(TextField):
    869     """A FormField that automatically converts its data to a datetime.time object.
    870     The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
    871     def __init__(self, field_name, is_required=False, validator_list=None):
    872         if validator_list is None: validator_list = []
    873         validator_list = [self.isValidTime] + validator_list
    874         TextField.__init__(self, field_name, length=8, max_length=8,
    875             is_required=is_required, validator_list=validator_list)
    876 
    877     def isValidTime(self, field_data, all_data):
    878         try:
    879             validators.isValidANSITime(field_data, all_data)
    880         except validators.ValidationError, e:
    881             raise validators.CriticalValidationError, e.messages
    882 
    883     def html2python(data):
    884         "Converts the field into a datetime.time object"
    885         import time, datetime
    886         try:
    887             part_list = data.split('.')
    888             try:
    889                 time_tuple = time.strptime(part_list[0], '%H:%M:%S')
    890             except ValueError: # seconds weren't provided
    891                 time_tuple = time.strptime(part_list[0], '%H:%M')
    892             t = datetime.time(*time_tuple[3:6])
    893             if (len(part_list) == 2):
    894                 t = t.replace(microsecond=int(part_list[1]))
    895             return t
    896         except (ValueError, TypeError, AttributeError):
    897             return None
    898     html2python = staticmethod(html2python)
    899 
    900 ####################
    901 # INTERNET-RELATED #
    902 ####################
    903 
    904 class EmailField(TextField):
    905     "A convenience FormField for validating e-mail addresses"
    906     def __init__(self, field_name, length=50, max_length=75, is_required=False, validator_list=None):
    907         if validator_list is None: validator_list = []
    908         validator_list = [self.isValidEmail] + validator_list
    909         TextField.__init__(self, field_name, length, max_length=max_length,
    910             is_required=is_required, validator_list=validator_list)
    911 
    912     def isValidEmail(self, field_data, all_data):
    913         try:
    914             validators.isValidEmail(field_data, all_data)
    915         except validators.ValidationError, e:
    916             raise validators.CriticalValidationError, e.messages
    917 
    918 class URLField(TextField):
    919     "A convenience FormField for validating URLs"
    920     def __init__(self, field_name, length=50, max_length=200, is_required=False, validator_list=None):
    921         if validator_list is None: validator_list = []
    922         validator_list = [self.isValidURL] + validator_list
    923         TextField.__init__(self, field_name, length=length, max_length=max_length,
    924             is_required=is_required, validator_list=validator_list)
    925 
    926     def isValidURL(self, field_data, all_data):
    927         try:
    928             validators.isValidURL(field_data, all_data)
    929         except validators.ValidationError, e:
    930             raise validators.CriticalValidationError, e.messages
    931 
    932 class IPAddressField(TextField):
    933     def __init__(self, field_name, length=15, max_length=15, is_required=False, validator_list=None):
    934         if validator_list is None: validator_list = []
    935         validator_list = [self.isValidIPAddress] + validator_list
    936         TextField.__init__(self, field_name, length=length, max_length=max_length,
    937             is_required=is_required, validator_list=validator_list)
    938 
    939     def isValidIPAddress(self, field_data, all_data):
    940         try:
    941             validators.isValidIPAddress4(field_data, all_data)
    942         except validators.ValidationError, e:
    943             raise validators.CriticalValidationError, e.messages
    944 
    945     def html2python(data):
    946         return data or None
    947     html2python = staticmethod(html2python)
    948 
    949 ####################
    950 # MISCELLANEOUS    #
    951 ####################
    952 
    953 class FilePathField(SelectField):
    954     "A SelectField whose choices are the files in a given directory."
    955     def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None, max_length=None):
    956         import os
    957         from django.db.models import BLANK_CHOICE_DASH
    958         if match is not None:
    959             import re
    960             match_re = re.compile(match)
    961         choices = not is_required and BLANK_CHOICE_DASH[:] or []
    962         if recursive:
    963             for root, dirs, files in os.walk(path):
    964                 for f in files:
    965                     if match is None or match_re.search(f):
    966                         f = os.path.join(root, f)
    967                         choices.append((f, f.replace(path, "", 1)))
    968         else:
    969             try:
    970                 for f in os.listdir(path):
    971                     full_file = os.path.join(path, f)
    972                     if os.path.isfile(full_file) and (match is None or match_re.search(f)):
    973                         choices.append((full_file, f))
    974             except OSError:
    975                 pass
    976         SelectField.__init__(self, field_name, choices, 1, is_required, validator_list)
    977 
    978 class PhoneNumberField(TextField):
    979     "A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
    980     def __init__(self, field_name, is_required=False, validator_list=None):
    981         if validator_list is None: validator_list = []
    982         validator_list = [self.isValidPhone] + validator_list
    983         TextField.__init__(self, field_name, length=12, max_length=12,
    984             is_required=is_required, validator_list=validator_list)
    985 
    986     def isValidPhone(self, field_data, all_data):
    987         try:
    988             validators.isValidPhone(field_data, all_data)
    989         except validators.ValidationError, e:
    990             raise validators.CriticalValidationError, e.messages
    991 
    992 class USStateField(TextField):
    993     "A convenience FormField for validating U.S. states (e.g. 'IL')"
    994     def __init__(self, field_name, is_required=False, validator_list=None):
    995         if validator_list is None: validator_list = []
    996         validator_list = [self.isValidUSState] + validator_list
    997         TextField.__init__(self, field_name, length=2, max_length=2,
    998             is_required=is_required, validator_list=validator_list)
    999 
    1000     def isValidUSState(self, field_data, all_data):
    1001         try:
    1002             validators.isValidUSState(field_data, all_data)
    1003         except validators.ValidationError, e:
    1004             raise validators.CriticalValidationError, e.messages
    1005 
    1006     def html2python(data):
    1007         if data:
    1008             return data.upper() # Should always be stored in upper case
    1009         return data
    1010     html2python = staticmethod(html2python)
    1011 
    1012 class CommaSeparatedIntegerField(TextField):
    1013     "A convenience FormField for validating comma-separated integer fields"
    1014     def __init__(self, field_name, max_length=None, is_required=False, validator_list=None):
    1015         if validator_list is None: validator_list = []
    1016         validator_list = [self.isCommaSeparatedIntegerList] + validator_list
    1017         TextField.__init__(self, field_name, length=20, max_length=max_length,
    1018             is_required=is_required, validator_list=validator_list)
    1019 
    1020     def isCommaSeparatedIntegerList(self, field_data, all_data):
    1021         try:
    1022             validators.isCommaSeparatedIntegerList(field_data, all_data)
    1023         except validators.ValidationError, e:
    1024             raise validators.CriticalValidationError, e.messages
    1025 
    1026     def render(self, data):
    1027         if data is None:
    1028             data = u''
    1029         elif isinstance(data, (list, tuple)):
    1030             data = u','.join(data)
    1031         return super(CommaSeparatedIntegerField, self).render(data)
    1032 
    1033 class RawIdAdminField(CommaSeparatedIntegerField):
    1034     def html2python(data):
    1035         if data:
    1036             return data.split(',')
    1037         else:
    1038             return []
    1039     html2python = staticmethod(html2python)
    1040 
    1041 class XMLLargeTextField(LargeTextField):
    1042     """
    1043     A LargeTextField with an XML validator. The schema_path argument is the
    1044     full path to a Relax NG compact schema to validate against.
    1045     """
    1046     def __init__(self, field_name, schema_path, **kwargs):
    1047         self.schema_path = schema_path
    1048         kwargs.setdefault('validator_list', []).insert(0, self.isValidXML)
    1049         LargeTextField.__init__(self, field_name, **kwargs)
    1050 
    1051     def isValidXML(self, field_data, all_data):
    1052         v = validators.RelaxNGCompact(self.schema_path)
    1053         try:
    1054             v(field_data, all_data)
    1055         except validators.ValidationError, e:
    1056             raise validators.CriticalValidationError, e.messages
  • docs/howto/custom-model-fields.txt

    diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
    index d1b7ddd..b46df75 100644
    a b What does a field class do?  
    108108All of Django's fields (and when we say *fields* in this document, we always
    109109mean model fields and not :ref:`form fields <ref-forms-fields>`) are subclasses
    110110of :class:`django.db.models.Field`. Most of the information that Django records
    111 about a field is common to all fields -- name, help text, validator lists,
    112 uniqueness and so forth. Storing all that information is handled by ``Field``.
    113 We'll get into the precise details of what ``Field`` can do later on; for now,
    114 suffice it to say that everything descends from ``Field`` and then customizes
    115 key pieces of the class behavior.
     111about a field is common to all fields -- name, help text, uniqueness and so
     112forth. Storing all that information is handled by ``Field``. We'll get into the
     113precise details of what ``Field`` can do later on; for now, suffice it to say
     114that everything descends from ``Field`` and then customizes key pieces of the
     115class behavior.
    116116
    117117It's important to realize that a Django field class is not what is stored in
    118118your model attributes. The model attributes contain normal Python objects. The
    parameters:  
    210210    * :attr:`~django.db.models.Field.unique_for_date`
    211211    * :attr:`~django.db.models.Field.unique_for_month`
    212212    * :attr:`~django.db.models.Field.unique_for_year`
    213     * :attr:`~django.db.models.Field.validator_list`
    214213    * :attr:`~django.db.models.Field.choices`
    215214    * :attr:`~django.db.models.Field.help_text`
    216215    * :attr:`~django.db.models.Field.db_column`
    output in some other place, outside of Django.  
    567566Converting field data for serialization
    568567~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    569568
    570 .. method:: flatten_data(self, follow, obj=None)
    571 
    572 .. admonition:: Subject to change
    573 
    574     Although implementing this method is necessary to allow field
    575     serialization, the API might change in the future.
    576 
    577 Returns a dictionary, mapping the field's attribute name to a flattened string
    578 version of the data. This method has some internal uses that aren't of interest
    579 to use here (mostly having to do with forms). For our purposes, it's sufficient
    580 to return a one item dictionary that maps the attribute name to a string.
     569.. method:: get_string_value(self, obj)
    581570
    582571This method is used by the serializers to convert the field into a string for
    583 output. You can ignore the input parameters for serialization purposes, although
    584 calling :meth:`Field._get_val_from_obj(obj)
    585 <django.db.models.Field._get_val_from_obj>` is the best way to get the value to
    586 serialize.
    587 
    588 For example, since our ``HandField`` uses strings for its data storage anyway,
    589 we can reuse some existing conversion code::
     572output. Calling :meth:``Field._get_val_from_obj(obj)`` is the best way to get the
     573value to serialize. For example, since our ``HandField`` uses strings for its
     574data storage anyway, we can reuse some existing conversion code::
    590575
    591576    class HandField(models.Field):
    592577        # ...
    593578
    594         def flatten_data(self, follow, obj=None):
     579        def get_string_value(self, obj):
    595580            value = self._get_val_from_obj(obj)
    596             return {self.attname: self.get_db_prep_value(value)}
     581            return self.get_db_prep_value(value)
    597582
    598583Some general advice
    599584--------------------
  • deleted file docs/obsolete/forms.txt

    diff --git a/docs/obsolete/forms.txt b/docs/obsolete/forms.txt
    deleted file mode 100644
    index 9bfd4a9..0000000
    + -  
    1 .. _obsolete-forms:
    2 
    3 ===============================
    4 Forms, fields, and manipulators
    5 ===============================
    6 
    7 Forwards-compatibility note
    8 ===========================
    9 
    10 The legacy forms/manipulators system described in this document is going to be
    11 replaced in the next Django release. If you're starting from scratch, we
    12 strongly encourage you not to waste your time learning this. Instead, learn and
    13 use the new :ref:`forms library <topics-forms-index>`.
    14 
    15 Introduction
    16 ============
    17 
    18 Once you've got a chance to play with Django's admin interface, you'll probably
    19 wonder if the fantastic form validation framework it uses is available to user
    20 code. It is, and this document explains how the framework works.
    21 
    22 We'll take a top-down approach to examining Django's form validation framework,
    23 because much of the time you won't need to use the lower-level APIs. Throughout
    24 this document, we'll be working with the following model, a "place" object::
    25 
    26     from django.db import models
    27 
    28     PLACE_TYPES = (
    29         (1, 'Bar'),
    30         (2, 'Restaurant'),
    31         (3, 'Movie Theater'),
    32         (4, 'Secret Hideout'),
    33     )
    34 
    35     class Place(models.Model):
    36         name = models.CharField(max_length=100)
    37         address = models.CharField(max_length=100, blank=True)
    38         city = models.CharField(max_length=50, blank=True)
    39         state = models.USStateField()
    40         zip_code = models.CharField(max_length=5, blank=True)
    41         place_type = models.IntegerField(choices=PLACE_TYPES)
    42 
    43         class Admin:
    44             pass
    45 
    46         def __unicode__(self):
    47             return self.name
    48 
    49 Defining the above class is enough to create an admin interface to a ``Place``,
    50 but what if you want to allow public users to submit places?
    51 
    52 Automatic Manipulators
    53 ======================
    54 
    55 The highest-level interface for object creation and modification is the
    56 **automatic Manipulator** framework. An automatic manipulator is a utility
    57 class tied to a given model that "knows" how to create or modify instances of
    58 that model and how to validate data for the object. Automatic Manipulators come
    59 in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally
    60 they are quite similar, but the former knows how to create new instances of the
    61 model, while the latter modifies existing instances. Both types of classes are
    62 automatically created when you define a new class::
    63 
    64     >>> from mysite.myapp.models import Place
    65     >>> Place.AddManipulator
    66     <class 'django.models.manipulators.AddManipulator'>
    67     >>> Place.ChangeManipulator
    68     <class 'django.models.manipulators.ChangeManipulator'>
    69 
    70 Using the ``AddManipulator``
    71 ----------------------------
    72 
    73 We'll start with the ``AddManipulator``.  Here's a very simple view that takes
    74 POSTed data from the browser and creates a new ``Place`` object::
    75 
    76     from django.shortcuts import render_to_response
    77     from django.http import Http404, HttpResponse, HttpResponseRedirect
    78     from django import oldforms as forms
    79     from mysite.myapp.models import Place
    80 
    81     def naive_create_place(request):
    82         """A naive approach to creating places; don't actually use this!"""
    83         # Create the AddManipulator.
    84         manipulator = Place.AddManipulator()
    85 
    86         # Make a copy of the POSTed data so that do_html2python can
    87         # modify it in place (request.POST is immutable).
    88         new_data = request.POST.copy()
    89 
    90         # Convert the request data (which will all be strings) into the
    91         # appropriate Python types for those fields.
    92         manipulator.do_html2python(new_data)
    93 
    94         # Save the new object.
    95         new_place = manipulator.save(new_data)
    96 
    97         # It worked!
    98         return HttpResponse("Place created: %s" % new_place)
    99 
    100 The ``naive_create_place`` example works, but as you probably can tell, this
    101 view has a number of problems:
    102 
    103     * No validation of any sort is performed. If, for example, the ``name`` field
    104       isn't given in ``request.POST``, the save step will cause a database error
    105       because that field is required. Ugly.
    106 
    107     * Even if you *do* perform validation, there's still no way to give that
    108       information to the user in any sort of useful way.
    109 
    110     * You'll have to separately create a form (and view) that submits to this
    111       page, which is a pain and is redundant.
    112 
    113 Let's dodge these problems momentarily to take a look at how you could create a
    114 view with a form that submits to this flawed creation view::
    115 
    116     def naive_create_place_form(request):
    117         """Simplistic place form view; don't actually use anything like this!"""
    118         # Create a FormWrapper object that the template can use. Ignore
    119         # the last two arguments to FormWrapper for now.
    120         form = forms.FormWrapper(Place.AddManipulator(), {}, {})
    121         return render_to_response('places/naive_create_form.html', {'form': form})
    122 
    123 (This view, as well as all the following ones, has the same imports as in the
    124 first example above.)
    125 
    126 The ``forms.FormWrapper`` object is a wrapper that templates can
    127 easily deal with to create forms. Here's the ``naive_create_form.html``
    128 template::
    129 
    130     {% extends "base.html" %}
    131 
    132     {% block content %}
    133     <h1>Create a place:</h1>
    134 
    135     <form method="post" action="../do_new/">
    136     <p><label for="id_name">Name:</label> {{ form.name }}</p>
    137     <p><label for="id_address">Address:</label> {{ form.address }}</p>
    138     <p><label for="id_city">City:</label> {{ form.city }}</p>
    139     <p><label for="id_state">State:</label> {{ form.state }}</p>
    140     <p><label for="id_zip_code">Zip:</label> {{ form.zip_code }}</p>
    141     <p><label for="id_place_type">Place type:</label> {{ form.place_type }}</p>
    142     <input type="submit" />
    143     </form>
    144     {% endblock %}
    145 
    146 Before we get back to the problems with these naive set of views, let's go over
    147 some salient points of the above template:
    148 
    149     * Field "widgets" are handled for you: ``{{ form.field }}`` automatically
    150       creates the "right" type of widget for the form, as you can see with the
    151       ``place_type`` field above.
    152 
    153     * There isn't a way just to spit out the form. You'll still need to define
    154       how the form gets laid out. This is a feature: Every form should be
    155       designed differently. Django doesn't force you into any type of mold.
    156       If you must use tables, use tables. If you're a semantic purist, you can
    157       probably find better HTML than in the above template.
    158 
    159     * To avoid name conflicts, the ``id`` values of form elements take the
    160       form "id_*fieldname*".
    161 
    162 By creating a creation form we've solved problem number 3 above, but we still
    163 don't have any validation. Let's revise the validation issue by writing a new
    164 creation view that takes validation into account::
    165 
    166     def create_place_with_validation(request):
    167         manipulator = Place.AddManipulator()
    168         new_data = request.POST.copy()
    169 
    170         # Check for validation errors
    171         errors = manipulator.get_validation_errors(new_data)
    172         manipulator.do_html2python(new_data)
    173         if errors:
    174             return render_to_response('places/errors.html', {'errors': errors})
    175         else:
    176             new_place = manipulator.save(new_data)
    177             return HttpResponse("Place created: %s" % new_place)
    178 
    179 In this new version, errors will be found -- ``manipulator.get_validation_errors``
    180 handles all the validation for you -- and those errors can be nicely presented
    181 on an error page (templated, of course)::
    182 
    183     {% extends "base.html" %}
    184 
    185     {% block content %}
    186 
    187     <h1>Please go back and correct the following error{{ errors|pluralize }}:</h1>
    188     <ul>
    189         {% for e in errors.items %}
    190         <li>Field "{{ e.0 }}": {{ e.1|join:", " }}</li>
    191         {% endfor %}
    192     </ul>
    193 
    194     {% endblock %}
    195 
    196 Still, this has its own problems:
    197 
    198     * There's still the issue of creating a separate (redundant) view for the
    199       submission form.
    200 
    201     * Errors, though nicely presented, are on a separate page, so the user will
    202       have to use the "back" button to fix errors. That's ridiculous and unusable.
    203 
    204 The best way to deal with these issues is to collapse the two views -- the form
    205 and the submission -- into a single view.  This view will be responsible for
    206 creating the form, validating POSTed data, and creating the new object (if the
    207 data is valid). An added bonus of this approach is that errors and the form will
    208 both be available on the same page, so errors with fields can be presented in
    209 context.
    210 
    211 .. admonition:: Philosophy:
    212 
    213     Finally, for the HTTP purists in the audience (and the authorship), this
    214     nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches
    215     the form, and POST creates the new object.
    216 
    217 Below is the finished view::
    218 
    219     def create_place(request):
    220         manipulator = Place.AddManipulator()
    221 
    222         if request.method == 'POST':
    223             # If data was POSTed, we're trying to create a new Place.
    224             new_data = request.POST.copy()
    225 
    226             # Check for errors.
    227             errors = manipulator.get_validation_errors(new_data)
    228             manipulator.do_html2python(new_data)
    229 
    230             if not errors:
    231                 # No errors. This means we can save the data!
    232                 new_place = manipulator.save(new_data)
    233 
    234                 # Redirect to the object's "edit" page. Always use a redirect
    235                 # after POST data, so that reloads don't accidentally create
    236                 # duplicate entries, and so users don't see the confusing
    237                 # "Repost POST data?" alert box in their browsers.
    238                 return HttpResponseRedirect("/places/edit/%i/" % new_place.id)
    239         else:
    240             # No POST, so we want a brand new form without any data or errors.
    241             errors = new_data = {}
    242 
    243         # Create the FormWrapper, template, context, response.
    244         form = forms.FormWrapper(manipulator, new_data, errors)
    245         return render_to_response('places/create_form.html', {'form': form})
    246 
    247 and here's the ``create_form`` template::
    248 
    249     {% extends "base.html" %}
    250 
    251     {% block content %}
    252     <h1>Create a place:</h1>
    253 
    254     {% if form.has_errors %}
    255     <h2>Please correct the following error{{ form.error_dict|pluralize }}:</h2>
    256     {% endif %}
    257 
    258     <form method="post" action=".">
    259     <p>
    260         <label for="id_name">Name:</label> {{ form.name }}
    261         {% if form.name.errors %}*** {{ form.name.errors|join:", " }}{% endif %}
    262     </p>
    263     <p>
    264         <label for="id_address">Address:</label> {{ form.address }}
    265         {% if form.address.errors %}*** {{ form.address.errors|join:", " }}{% endif %}
    266     </p>
    267     <p>
    268         <label for="id_city">City:</label> {{ form.city }}
    269         {% if form.city.errors %}*** {{ form.city.errors|join:", " }}{% endif %}
    270     </p>
    271     <p>
    272         <label for="id_state">State:</label> {{ form.state }}
    273         {% if form.state.errors %}*** {{ form.state.errors|join:", " }}{% endif %}
    274     </p>
    275     <p>
    276         <label for="id_zip_code">Zip:</label> {{ form.zip_code }}
    277         {% if form.zip_code.errors %}*** {{ form.zip_code.errors|join:", " }}{% endif %}
    278     </p>
    279     <p>
    280         <label for="id_place_type">Place type:</label> {{ form.place_type }}
    281         {% if form.place_type.errors %}*** {{ form.place_type.errors|join:", " }}{% endif %}
    282     </p>
    283     <input type="submit" />
    284     </form>
    285     {% endblock %}
    286 
    287 The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``)
    288 deserve some mention.
    289 
    290 The first is any "default" data to be used as values for the fields. Pulling
    291 the data from ``request.POST``, as is done above, makes sure that if there are
    292 errors, the values the user put in aren't lost. If you try the above example,
    293 you'll see this in action.
    294 
    295 The second argument is the error list retrieved from
    296 ``manipulator.get_validation_errors``.  When passed into the ``FormWrapper``,
    297 this gives each field an ``errors`` item (which is a list of error messages
    298 associated with the field) as well as a ``html_error_list`` item, which is a
    299 ``<ul>`` of error messages. The above template uses these error items to
    300 display a simple error message next to each field. The error list is saved as
    301 an ``error_dict`` attribute of the ``FormWrapper`` object.
    302 
    303 Using the ``ChangeManipulator``
    304 -------------------------------
    305 
    306 The above has covered using the ``AddManipulator`` to create a new object. What
    307 about editing an existing one? It's shockingly similar to creating a new one::
    308 
    309     def edit_place(request, place_id):
    310         # Get the place in question from the database and create a
    311         # ChangeManipulator at the same time.
    312         try:
    313             manipulator = Place.ChangeManipulator(place_id)
    314         except Place.DoesNotExist:
    315             raise Http404
    316 
    317         # Grab the Place object in question for future use.
    318         place = manipulator.original_object
    319 
    320         if request.method == 'POST':
    321             new_data = request.POST.copy()
    322             errors = manipulator.get_validation_errors(new_data)
    323             manipulator.do_html2python(new_data)
    324             if not errors:
    325                 manipulator.save(new_data)
    326 
    327                 # Do a post-after-redirect so that reload works, etc.
    328                 return HttpResponseRedirect("/places/edit/%i/" % place.id)
    329         else:
    330             errors = {}
    331             # This makes sure the form accurate represents the fields of the place.
    332             new_data = manipulator.flatten_data()
    333 
    334         form = forms.FormWrapper(manipulator, new_data, errors)
    335         return render_to_response('places/edit_form.html', {'form': form, 'place': place})
    336 
    337 The only real differences are:
    338 
    339     * We create a ``ChangeManipulator`` instead of an ``AddManipulator``.
    340       The argument to a ``ChangeManipulator`` is the ID of the object
    341       to be changed. As you can see, the initializer will raise an
    342       ``ObjectDoesNotExist`` exception if the ID is invalid.
    343 
    344     * ``ChangeManipulator.original_object`` stores the instance of the
    345       object being edited.
    346 
    347     * We set ``new_data`` based upon ``flatten_data()`` from the manipulator.
    348       ``flatten_data()`` takes the data from the original object under
    349       manipulation, and converts it into a data dictionary that can be used
    350       to populate form elements with the existing values for the object.
    351 
    352     * The above example uses a different template, so create and edit can be
    353       "skinned" differently if needed, but the form chunk itself is completely
    354       identical to the one in the create form above.
    355 
    356 The astute programmer will notice the add and create functions are nearly
    357 identical and could in fact be collapsed into a single view. This is left as an
    358 exercise for said programmer.
    359 
    360 (However, the even-more-astute programmer will take heed of the note at the top
    361 of this document and check out the :ref:`generic views <ref-generic-views>`
    362 documentation if all she wishes to do is this type of simple create/update.)
    363 
    364 Custom forms and manipulators
    365 =============================
    366 
    367 All the above is fine and dandy if you just want to use the automatically
    368 created manipulators. But the coolness doesn't end there: You can easily create
    369 your own custom manipulators for handling custom forms.
    370 
    371 Custom manipulators are pretty simple. Here's a manipulator that you might use
    372 for a "contact" form on a website::
    373 
    374     from django import oldforms as forms
    375 
    376     urgency_choices = (
    377         (1, "Extremely urgent"),
    378         (2, "Urgent"),
    379         (3, "Normal"),
    380         (4, "Unimportant"),
    381     )
    382 
    383     class ContactManipulator(forms.Manipulator):
    384         def __init__(self):
    385             self.fields = (
    386                 forms.EmailField(field_name="from", is_required=True),
    387                 forms.TextField(field_name="subject", length=30, max_length=200, is_required=True),
    388                 forms.SelectField(field_name="urgency", choices=urgency_choices),
    389                 forms.LargeTextField(field_name="contents", is_required=True),
    390             )
    391 
    392 A certain similarity to Django's models should be apparent. The only required
    393 method of a custom manipulator is ``__init__`` which must define the fields
    394 present in the manipulator.  See the ``django.forms`` module for
    395 all the form fields provided by Django.
    396 
    397 You use this custom manipulator exactly as you would use an auto-generated one.
    398 Here's a simple function that might drive the above form::
    399 
    400     def contact_form(request):
    401         manipulator = ContactManipulator()
    402         if request.method == 'POST':
    403             new_data = request.POST.copy()
    404             errors = manipulator.get_validation_errors(new_data)
    405             manipulator.do_html2python(new_data)
    406             if not errors:
    407 
    408                 # Send e-mail using new_data here...
    409 
    410                 return HttpResponseRedirect("/contact/thankyou/")
    411         else:
    412             errors = new_data = {}
    413         form = forms.FormWrapper(manipulator, new_data, errors)
    414         return render_to_response('contact_form.html', {'form': form})
    415 
    416 Implementing ``flatten_data`` for custom manipulators
    417 ------------------------------------------------------
    418 
    419 It is possible (although rarely needed) to replace the default automatically
    420 created manipulators on a model with your own custom manipulators. If you do
    421 this and you are intending to use those models in generic views, you should
    422 also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement.
    423 This should act like the default ``flatten_data`` and return a dictionary
    424 mapping field names to their values, like so::
    425 
    426     def flatten_data(self):
    427         obj = self.original_object
    428         return dict(
    429             from = obj.from,
    430             subject = obj.subject,
    431             ...
    432         )
    433 
    434 In this way, your new change manipulator will act exactly like the default
    435 version.
    436 
    437 ``FileField`` and ``ImageField`` special cases
    438 ==============================================
    439 
    440 Dealing with ``FileField`` and ``ImageField`` objects is a little more
    441 complicated.
    442 
    443 First, you'll need to make sure that your ``<form>`` element correctly defines
    444 the ``enctype`` as ``"multipart/form-data"``, in order to upload files::
    445 
    446   <form enctype="multipart/form-data" method="post" action="/foo/">
    447 
    448 Next, you'll need to treat the field in the template slightly differently. A
    449 ``FileField`` or ``ImageField`` is represented by *two* HTML form elements.
    450 
    451 For example, given this field in a model::
    452 
    453    photo = model.ImageField('/path/to/upload/location')
    454 
    455 You'd need to display two formfields in the template::
    456 
    457    <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p>
    458 
    459 The first bit (``{{ form.photo }}``) displays the currently-selected file,
    460 while the second (``{{ form.photo_file }}``) actually contains the file upload
    461 form field. Thus, at the validation layer you need to check the ``photo_file``
    462 key.
    463 
    464 Finally, in your view, make sure to access ``request.FILES``, rather than
    465 ``request.POST``, for the uploaded files. This is necessary because
    466 ``request.POST`` does not contain file-upload data.
    467 
    468 For example, following the ``new_data`` convention, you might do something like
    469 this::
    470 
    471    new_data = request.POST.copy()
    472    new_data.update(request.FILES)
    473 
    474 Validators
    475 ==========
    476 
    477 One useful feature of manipulators is the automatic validation. Validation is
    478 done using a simple validation API: A validator is a callable that raises a
    479 ``ValidationError`` if there's something wrong with the data.
    480 ``django.core.validators`` defines a host of validator functions (see below),
    481 but defining your own couldn't be easier::
    482 
    483     from django.core import validators
    484     from django import oldforms as forms
    485 
    486     class ContactManipulator(forms.Manipulator):
    487         def __init__(self):
    488             self.fields = (
    489                 # ... snip fields as above ...
    490                 forms.EmailField(field_name="to", validator_list=[self.isValidToAddress])
    491             )
    492 
    493         def isValidToAddress(self, field_data, all_data):
    494             if not field_data.endswith("@example.com"):
    495                 raise validators.ValidationError("You can only send messages to example.com e-mail addresses.")
    496 
    497 Above, we've added a "to" field to the contact form, but required that the "to"
    498 address end with "@example.com" by adding the ``isValidToAddress`` validator to
    499 the field's ``validator_list``.
    500 
    501 The arguments to a validator function take a little explanation.  ``field_data``
    502 is the value of the field in question, and ``all_data`` is a dictionary of all
    503 the data being validated.
    504 
    505 .. admonition:: Note::
    506 
    507     At the point validators are called all data will still be
    508     strings (as ``do_html2python`` hasn't been called yet).
    509 
    510 Also, because consistency in user interfaces is important, we strongly urge you
    511 to put punctuation at the end of your validation messages.
    512 
    513 When are validators called?
    514 ---------------------------
    515 
    516 After a form has been submitted, Django validates each field in turn. First,
    517 if the field is required, Django checks that it is present and non-empty. Then,
    518 if that test passes *and the form submission contained data* for that field, all
    519 the validators for that field are called in turn. The emphasized portion in the
    520 last sentence is important: if a form field is not submitted (because it
    521 contains no data -- which is normal HTML behavior), the validators are not
    522 run against the field.
    523 
    524 This feature is particularly important for models using
    525 ``models.BooleanField`` or custom manipulators using things like
    526 ``forms.CheckBoxField``. If the checkbox is not selected, it will not
    527 contribute to the form submission.
    528 
    529 If you would like your validator to run *always*, regardless of whether its
    530 attached field contains any data, set the ``always_test`` attribute on the
    531 validator function. For example::
    532 
    533     def my_custom_validator(field_data, all_data):
    534         # ...
    535     my_custom_validator.always_test = True
    536 
    537 This validator will always be executed for any field it is attached to.
    538 
    539 Ready-made validators
    540 ---------------------
    541 
    542 Writing your own validator is not difficult, but there are some situations
    543 that come up over and over again. Django comes with a number of validators
    544 that can be used directly in your code. All of these functions and classes
    545 reside in ``django/core/validators.py``.
    546 
    547 The following validators should all be self-explanatory. Each one provides a
    548 check for the given property:
    549 
    550     * isAlphaNumeric
    551     * isAlphaNumericURL
    552     * isSlug
    553     * isLowerCase
    554     * isUpperCase
    555     * isCommaSeparatedIntegerList
    556     * isCommaSeparatedEmailList
    557     * isValidIPAddress4
    558     * isNotEmpty
    559     * isOnlyDigits
    560     * isNotOnlyDigits
    561     * isInteger
    562     * isOnlyLetters
    563     * isValidANSIDate
    564     * isValidANSITime
    565     * isValidEmail
    566     * isValidFloat
    567     * isValidImage
    568     * isValidImageURL
    569     * isValidPhone
    570     * isValidQuicktimeVideoURL
    571     * isValidURL
    572     * isValidHTML
    573     * isWellFormedXml
    574     * isWellFormedXmlFragment
    575     * isExistingURL
    576     * isValidUSState
    577     * hasNoProfanities
    578 
    579 There are also a group of validators that are slightly more flexible. For
    580 these validators, you create a validator instance, passing in the parameters
    581 described below. The returned object is a callable that can be used as a
    582 validator.
    583 
    584 For example::
    585 
    586     from django.core import validators
    587     from django import oldforms as forms
    588 
    589     power_validator = validators.IsAPowerOf(2)
    590 
    591     class InstallationManipulator(forms.Manipulator)
    592         def __init__(self):
    593             self.fields = (
    594                 ...
    595                 forms.IntegerField(field_name = "size", validator_list=[power_validator])
    596             )
    597 
    598 Here, ``validators.IsAPowerOf(...)`` returned something that could be used as
    599 a validator (in this case, a check that a number was a power of 2).
    600 
    601 Each of the standard validators that take parameters have an optional final
    602 argument (``error_message``) that is the message returned when validation
    603 fails. If no message is passed in, a default message is used.
    604 
    605 ``AlwaysMatchesOtherField``
    606     Takes a field name and the current field is valid if and only if its value
    607     matches the contents of the other field.
    608 
    609 ``ValidateIfOtherFieldEquals``
    610     Takes three parameters: ``other_field``, ``other_value`` and
    611     ``validator_list``, in that order. If ``other_field`` has a value of
    612     ``other_value``, then the validators in ``validator_list`` are all run
    613     against the current field.
    614 
    615 ``RequiredIfOtherFieldGiven``
    616     Takes a field name of the current field is only required if the other
    617     field has a value.
    618 
    619 ``RequiredIfOtherFieldsGiven``
    620     Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of
    621     field names and if any one of the supplied fields has a value provided,
    622     the current field being validated is required.
    623 
    624 ``RequiredIfOtherFieldNotGiven``
    625     Takes the name of the other field and this field is only required if the
    626     other field has no value.
    627 
    628 ``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual``
    629     Each of these validator classes takes a field name and a value (in that
    630     order). If the given field does (or does not have, in the latter case) the
    631     given value, then the current field being validated is required.
    632 
    633     An optional ``other_label`` argument can be passed which, if given, is used
    634     in error messages instead of the value. This allows more user friendly error
    635     messages if the value itself is not descriptive enough.
    636 
    637     Note that because validators are called before any ``do_html2python()``
    638     functions, the value being compared against is a string. So
    639     ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst
    640     ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the
    641     equality test succeeding.
    642 
    643 ``IsLessThanOtherField``
    644     Takes a field name and validates that the current field being validated
    645     has a value that is less than (or equal to) the other field's value.
    646     Again, comparisons are done using strings, so be cautious about using
    647     this function to compare data that should be treated as another type. The
    648     string "123" is less than the string "2", for example. If you don't want
    649     string comparison here, you will need to write your own validator.
    650 
    651 ``NumberIsInRange``
    652     Takes two boundary numbers, ``lower`` and ``upper``, and checks that the
    653     field is greater than ``lower`` (if given) and less than ``upper`` (if
    654     given).
    655 
    656     Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow
    657     values of both 10 and 20. This validator only checks numeric values
    658     (e.g., float and integer values).
    659 
    660 ``IsAPowerOf``
    661     Takes an integer argument and when called as a validator, checks that the
    662     field being validated is a power of the integer.
    663 
    664 ``IsValidDecimal``
    665     Takes a maximum number of digits and number of decimal places (in that
    666     order) and validates whether the field is a decimal with no more than the
    667     maximum number of digits and decimal places.
    668 
    669 ``MatchesRegularExpression``
    670     Takes a regular expression (a string) as a parameter and validates the
    671     field value against it.
    672 
    673 ``AnyValidator``
    674     Takes a list of validators as a parameter. At validation time, if the
    675     field successfully validates against any one of the validators, it passes
    676     validation. The validators are tested in the order specified in the
    677     original list.
    678 
    679 ``URLMimeTypeCheck``
    680     Used to validate URL fields. Takes a list of MIME types (such as
    681     ``text/plain``) at creation time. At validation time, it verifies that the
    682     field is indeed a URL and then tries to retrieve the content at the URL.
    683     Validation succeeds if the content could be retrieved and it has a content
    684     type from the list used to create the validator.
    685 
    686 ``RelaxNGCompact``
    687     Used to validate an XML document against a Relax NG compact schema. Takes a
    688     file path to the location of the schema and an optional root element (which
    689     is wrapped around the XML fragment before validation, if supplied). At
    690     validation time, the XML fragment is validated against the schema using the
    691     executable specified in the ``JING_PATH`` setting (see the :ref:`settings
    692     <ref-settings>` document for more details).
  • deleted file docs/obsolete/newforms-migration.txt

    diff --git a/docs/obsolete/newforms-migration.txt b/docs/obsolete/newforms-migration.txt
    deleted file mode 100644
    index da56b57..0000000
    + -  
    1 .. _howto-newforms-migration:
    2 
    3 Migrating from "oldforms" to "newforms"
    4 =======================================
    5 
    6 :mod:`django.newforms` is new in Django's 0.96 release, but, as it won't be new
    7 forever. We plan to rename it to ``django.forms`` in next official release. The
    8 current ``django.forms`` package will be available as ``django.oldforms`` until
    9 Django 1.0, when we plan to remove it for good.
    10 
    11 If you're using "old" forms -- and if you started using Django after 0.96 you're
    12 probably not -- you need to read this document and understand this migration
    13 plan.
    14 
    15     * The old forms framework (the current ``django.forms``) has been copied to
    16       ``django.oldforms``. Thus, you can start upgrading your code *now*,
    17       rather than waiting for the future backwards-incompatible change, by
    18       changing your import statements like this::
    19 
    20           from django import forms             # old
    21           from django import oldforms as forms # new
    22 
    23     * In the next Django release, we will move the current ``django.newforms``
    24       to ``django.forms``. This will be a backwards-incompatible change, and
    25       anybody who is still using the old version of ``django.forms`` at that
    26       time will need to change their import statements, as described in the
    27       previous bullet.
    28 
    29     * We will remove ``django.oldforms`` in Django 1.0. It will continue to be
    30       available from older tags in our SVN repository, but it will not be
    31       consider part of Django, and will not be supported..
    32 
    33 With this in mind, we recommend you use the following import statement when
    34 using ``django.newforms``::
    35 
    36     from django import newforms as forms
    37 
    38 This way, your code can refer to the ``forms`` module, and when
    39 ``django.newforms`` is renamed to ``django.forms``, you'll only have to change
    40 your ``import`` statements.
    41 
    42 If you prefer "``import *``" syntax, you can do the following::
    43 
    44     from django.newforms import *
    45 
    46 This will import all fields, widgets, form classes and other various utilities
    47 into your local namespace. Some people find this convenient; others find it
    48 too messy. The choice is yours.
  • docs/ref/models/fields.txt

    diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
    index 7faa07d..909bfef 100644
    a b hacking :attr:`~Field.choices` to be dynamic, you're probably better off using a  
    145145proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is
    146146meant for static data that doesn't change much, if ever.
    147147
    148 ``core``
    149 --------
    150 
    151 .. attribute:: Field.core
    152 
    153 For objects that are edited inline to a related object.
    154 
    155 In the Django admin, if all "core" fields in an inline-edited object are
    156 cleared, the object will be deleted.
    157 
    158 It is an error to have an inline-editable relation without at least one
    159 ``core=True`` field.
    160 
    161 Please note that each field marked "core" is treated as a required field by the
    162 Django admin site. Essentially, this means you should put ``core=True`` on all
    163 required fields in your related object that is being edited inline.
    164 
    165148``db_column``
    166149-------------
    167150
    respect to the month.  
    287270
    288271Like :attr:`~Field.unique_for_date` and :attr:`~Field.unique_for_month`.
    289272
    290 ``validator_list``
    291 ------------------
    292 
    293 .. attribute:: Field.validator_list
    294 
    295 A list of extra validators to apply to the field. Each should be a callable that
    296 takes the parameters ``field_data, all_data`` and raises
    297 :exc:`django.core.validators.ValidationError` for errors.
    298 
    299273.. _model-field-types:
    300274
    301275Field types
    that control how the relationship functions.  
    913887
    914888The semantics of one-to-one relationships will be changing soon, so we don't
    915889recommend you use them. If that doesn't scare you away, however,
    916 :class:`OneToOneField` takes the same options that :class:`ForeignKey` does,
    917 except for the various :attr:`~ForeignKey.edit_inline`-related options.
     890:class:`OneToOneField` takes the same options that :class:`ForeignKey` does.
  • docs/topics/forms/modelforms.txt

    diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
    index f93bd0f..d161b3f 100644
    a b the full list of conversions:  
    6767                                     (from ``django.contrib.localflavor.us``)
    6868    ``PositiveIntegerField``         ``IntegerField``
    6969    ``PositiveSmallIntegerField``    ``IntegerField``
    70     ``SlugField``                    ``RegexField`` accepting only letters,
    71                                      numbers, underscores and hyphens
     70    ``SlugField``                    ``SlugField``
    7271    ``SmallIntegerField``            ``IntegerField``
    7372    ``TextField``                    ``CharField`` with ``widget=Textarea``
    7473    ``TimeField``                    ``TimeField``
  • docs/topics/testing.txt

    diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
    index c8c06d9..42fa1fb 100644
    a b applications:  
    899899    rendered on the form.
    900900
    901901    ``form`` is the name the ``Form`` instance was given in the template
    902     context. Note that this works only for ``forms.Form`` instances, not
    903     ``oldforms.Form`` instances.
     902    context.
    904903
    905904    ``field`` is the name of the field on the form to check. If ``field``
    906905    has a value of ``None``, non-field errors (errors you can access via
  • tests/modeltests/field_subclassing/models.py

    diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py
    index baf07a0..c776146 100644
    a b class SmallField(models.Field):  
    5353            return []
    5454        raise FieldError('Invalid lookup type: %r' % lookup_type)
    5555
    56     def flatten_data(self, follow, obj=None):
    57         return {self.attname: force_unicode(self._get_val_from_obj(obj))}
    58 
    5956class MyModel(models.Model):
    6057    name = models.CharField(max_length=10)
    6158    data = SmallField('small field')
  • tests/modeltests/invalid_models/models.py

    diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py
    index abd72b1..2817208 100644
    a b class Target(models.Model):  
    2323    clash1_set = models.CharField(max_length=10)
    2424
    2525class Clash1(models.Model):
    26     src_safe = models.CharField(max_length=10, core=True)
     26    src_safe = models.CharField(max_length=10)
    2727
    2828    foreign = models.ForeignKey(Target)
    2929    m2m = models.ManyToManyField(Target)
    3030
    3131class Clash2(models.Model):
    32     src_safe = models.CharField(max_length=10, core=True)
     32    src_safe = models.CharField(max_length=10)
    3333
    3434    foreign_1 = models.ForeignKey(Target, related_name='id')
    3535    foreign_2 = models.ForeignKey(Target, related_name='src_safe')
    class Target2(models.Model):  
    4646    clashm2m_set = models.ManyToManyField(Target)
    4747
    4848class Clash3(models.Model):
    49     src_safe = models.CharField(max_length=10, core=True)
     49    src_safe = models.CharField(max_length=10)
    5050
    5151    foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt')
    5252    foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt')
    class ClashM2M(models.Model):  
    6161    m2m = models.ManyToManyField(Target2)
    6262
    6363class SelfClashForeign(models.Model):
    64     src_safe = models.CharField(max_length=10, core=True)
     64    src_safe = models.CharField(max_length=10)
    6565    selfclashforeign = models.CharField(max_length=10)
    6666
    6767    selfclashforeign_set = models.ForeignKey("SelfClashForeign")
  • deleted file tests/modeltests/manipulators/models.py

    diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py
    deleted file mode 100644
    index 0adee9e..0000000
    + -  
    1 # coding: utf-8
    2 """
    3 27. Default manipulators
    4 
    5 Each model gets an ``AddManipulator`` and ``ChangeManipulator`` by default.
    6 """
    7 
    8 from django.db import models
    9 
    10 class Musician(models.Model):
    11     first_name = models.CharField(max_length=30)
    12     last_name = models.CharField(max_length=30)
    13 
    14     def __unicode__(self):
    15         return u"%s %s" % (self.first_name, self.last_name)
    16 
    17 class Album(models.Model):
    18     name = models.CharField(max_length=100)
    19     musician = models.ForeignKey(Musician)
    20     release_date = models.DateField(blank=True, null=True)
    21 
    22     def __unicode__(self):
    23         return self.name
    24 
    25 __test__ = {'API_TESTS':u"""
    26 >>> from django.utils.datastructures import MultiValueDict
    27 
    28 # Create a Musician object via the default AddManipulator.
    29 >>> man = Musician.AddManipulator()
    30 >>> data = MultiValueDict({'first_name': ['Ella'], 'last_name': ['Fitzgerald']})
    31 
    32 >>> man.get_validation_errors(data)
    33 {}
    34 >>> man.do_html2python(data)
    35 >>> m1 = man.save(data)
    36 
    37 # Verify it worked.
    38 >>> Musician.objects.all()
    39 [<Musician: Ella Fitzgerald>]
    40 >>> [m1] == list(Musician.objects.all())
    41 True
    42 
    43 # Attempt to add a Musician without a first_name.
    44 >>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name']
    45 [u'This field is required.']
    46 
    47 # Attempt to add a Musician without a first_name and last_name.
    48 >>> errors = man.get_validation_errors(MultiValueDict({}))
    49 >>> errors['first_name']
    50 [u'This field is required.']
    51 >>> errors['last_name']
    52 [u'This field is required.']
    53 
    54 # Attempt to create an Album without a name or musician.
    55 >>> man = Album.AddManipulator()
    56 >>> errors = man.get_validation_errors(MultiValueDict({}))
    57 >>> errors['musician']
    58 [u'This field is required.']
    59 >>> errors['name']
    60 [u'This field is required.']
    61 
    62 # Attempt to create an Album with an invalid musician.
    63 >>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
    64 >>> errors['musician']
    65 [u"Select a valid choice; 'foo' is not in [u'', u'1']."]
    66 
    67 # Attempt to create an Album with an invalid release_date.
    68 >>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
    69 >>> errors['release_date']
    70 [u'Enter a valid date in YYYY-MM-DD format.']
    71 
    72 # Create an Album without a release_date (because it's optional).
    73 >>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})
    74 >>> man.get_validation_errors(data)
    75 {}
    76 >>> man.do_html2python(data)
    77 >>> a1 = man.save(data)
    78 
    79 # Verify it worked.
    80 >>> Album.objects.all()
    81 [<Album: Ella and Basie>]
    82 >>> Album.objects.get().musician
    83 <Musician: Ella Fitzgerald>
    84 
    85 # Create an Album with a release_date.
    86 >>> data = MultiValueDict({'name': ['Ultimate Ella'], 'musician': ['1'], 'release_date': ['2005-02-13']})
    87 >>> man.get_validation_errors(data)
    88 {}
    89 >>> man.do_html2python(data)
    90 >>> a2 = man.save(data)
    91 
    92 # Verify it worked.
    93 >>> Album.objects.order_by('name')
    94 [<Album: Ella and Basie>, <Album: Ultimate Ella>]
    95 >>> a2 = Album.objects.get(pk=2)
    96 >>> a2
    97 <Album: Ultimate Ella>
    98 >>> a2.release_date
    99 datetime.date(2005, 2, 13)
    100 
    101 # Test isValidFloat Unicode coercion
    102 >>> from django.core.validators import isValidFloat, ValidationError
    103 >>> try: isValidFloat(u"ä", None)
    104 ... except ValidationError: pass
    105 """}
  • tests/modeltests/mutually_referential/models.py

    diff --git a/tests/modeltests/mutually_referential/models.py b/tests/modeltests/mutually_referential/models.py
    index 5176721..2cbaa4b 100644
    a b Strings can be used instead of model literals to set up "lazy" relations.  
    77from django.db.models import *
    88
    99class Parent(Model):
    10     name = CharField(max_length=100, core=True)
     10    name = CharField(max_length=100)
    1111   
    1212    # Use a simple string for forward declarations.
    1313    bestchild = ForeignKey("Child", null=True, related_name="favoured_by")
Back to Top