Ticket #7742: 7742.2008-08-26.3.diff
File 7742.2008-08-26.3.diff, 154.4 KB (added by , 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_ 85 85 perms_needed.add(related.opts.verbose_name) 86 86 # We don't care about populating deleted_objects now. 87 87 continue 88 if related.field.rel.edit_inline ornot has_admin:88 if not has_admin: 89 89 # Don't display link to edit, because it either has no 90 90 # admin or is edited inline. 91 91 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_ 101 101 has_related_objs = False 102 102 for sub_obj in getattr(obj, rel_opts_name).all(): 103 103 has_related_objs = True 104 if related.field.rel.edit_inline ornot has_admin:104 if not has_admin: 105 105 # Don't display link to edit, because it either has no 106 106 # admin or is edited inline. 107 107 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_ 132 132 133 133 if has_related_objs: 134 134 for sub_obj in rel_objs.all(): 135 if related.field.rel.edit_inline ornot has_admin:135 if not has_admin: 136 136 # Don't display link to edit, because it either has no 137 137 # admin or is edited inline. 138 138 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 2 2 Classes allowing "generic" relations through ContentType and object-id fields. 3 3 """ 4 4 5 from django import oldforms6 5 from django.core.exceptions import ObjectDoesNotExist 7 6 from django.db import connection 8 7 from django.db.models import signals 9 8 from django.db import models 10 9 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel 11 10 from django.db.models.loading import get_model 12 from django.utils.functional import curry13 14 11 from django.forms import ModelForm 15 12 from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance 16 13 from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets 14 from django.utils.encoding import smart_unicode 17 15 18 16 class GenericForeignKey(object): 19 17 """ … … class GenericRelation(RelatedField, Field): 120 118 kwargs['serialize'] = False 121 119 Field.__init__(self, **kwargs) 122 120 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 127 121 def get_choices_default(self): 128 122 return Field.get_choices(self, include_blank=False) 129 123 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]) 136 127 137 128 def m2m_db_table(self): 138 129 return self.rel.to._meta.db_table … … class GenericRel(ManyToManyRel): 280 271 self.to = to 281 272 self.related_name = related_name 282 273 self.limit_choices_to = limit_choices_to or {} 283 self.edit_inline = False284 274 self.symmetrical = symmetrical 285 275 self.multiple = True 286 276 … … class BaseGenericInlineFormSet(BaseModelFormSet): 290 280 """ 291 281 ct_field_name = "content_type" 292 282 ct_fk_field_name = "object_id" 293 283 294 284 def __init__(self, data=None, files=None, instance=None, save_as_new=None): 295 285 opts = self.model._meta 296 286 self.instance = instance … … class GenericStackedInline(GenericInlineModelAdmin): 385 375 386 376 class GenericTabularInline(GenericInlineModelAdmin): 387 377 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): 57 57 """ 58 58 Convert a field's value to a string. 59 59 """ 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)) 66 61 67 62 def start_serialization(self): 68 63 """ -
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 9 9 from django.db.models.fields import * 10 10 from django.db.models.fields.subclassing import SubfieldBase 11 11 from django.db.models.fields.files import FileField, ImageField 12 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel , TABULAR, STACKED12 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel 13 13 from django.db.models import signals 14 14 15 15 # 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: 8 8 except NameError: 9 9 from sets import Set as set # Python 2.3 fallback. 10 10 11 import django.db.models.manipulators # Imported to register signal handler. 12 import django.db.models.manager # Ditto. 11 import django.db.models.manager # Imported to register signal handler. 13 12 from django.core import validators 14 13 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError 15 14 from django.db.models.fields import AutoField … … class Model(object): 320 319 321 320 # First, try an UPDATE. If that doesn't update anything, do an INSERT. 322 321 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 326 323 record_exists = True 327 324 manager = cls._default_manager 328 325 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 13 13 from django.dispatch import dispatcher 14 14 from django.conf import settings 15 15 from django.core import validators 16 from django import oldforms17 16 from django import forms 18 17 from django.core.exceptions import ObjectDoesNotExist 19 18 from django.utils.datastructures import DictWrapper … … BLANK_CHOICE_NONE = [("", "None")] 34 33 class FieldDoesNotExist(Exception): 35 34 pass 36 35 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 return44 if getattr(self, 'original_object', None) and self.original_object._get_pk_val() == old_obj._get_pk_val():45 return46 raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name}47 48 36 # A guide to Field parameters: 49 37 # 50 38 # * name: The name of the field specifed in the model. … … class Field(object): 73 61 74 62 def __init__(self, verbose_name=None, name=None, primary_key=False, 75 63 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, 80 68 auto_created=False): 81 69 self.name = name 82 70 self.verbose_name = verbose_name … … class Field(object): 87 75 # option whenever '' is a possible value. 88 76 if self.empty_strings_allowed and connection.features.interprets_empty_strings_as_nulls: 89 77 self.null = True 90 self. core, self.rel, self.default = core,rel, default78 self.rel, self.default = rel, default 91 79 self.editable = editable 92 80 self.serialize = serialize 93 self.validator_list = validator_list or []94 81 self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month 95 82 self.unique_for_year = unique_for_year 96 83 self._choices = choices or [] … … class Field(object): 252 239 return None 253 240 return "" 254 241 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_length265 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. It277 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 keys300 # are a special case, and fields in a related context should set this301 # 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 rel304 305 # BooleanFields (CheckboxFields) are a special case. They don't take306 # 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 fields311 # 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 327 242 def get_validator_unique_lookup_type(self): 328 243 return '%s__exact' % self.name 329 244 330 def get_manipulator_new_data(self, new_data, rel=False):331 """332 Given the full new_data dictionary (from the manipulator), returns this333 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 = None340 return val341 342 245 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 343 246 """Returns choices with a default blank choices included, for use 344 247 as SelectField choices for this field.""" … … class Field(object): 366 269 else: 367 270 return self.get_default() 368 271 369 def flatten_data(self, follow, obj=None):272 def get_string_value(self, obj): 370 273 """ 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. 374 275 """ 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)) 382 277 383 278 def bind(self, fieldmapping, original, bound_field_class): 384 279 return bound_field_class(self, fieldmapping, original) … … class AutoField(Field): 439 334 return None 440 335 return int(value) 441 336 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 called452 # Not in main change pages453 # ignored in related context454 if not rel:455 return None456 return Field.get_manipulator_new_data(self, new_data, rel)457 458 337 def contribute_to_class(self, cls, name): 459 338 assert not cls._meta.has_auto_field, "A model can't have more than one AutoField." 460 339 super(AutoField, self).contribute_to_class(cls, name) … … class BooleanField(Field): 485 364 return None 486 365 return bool(value) 487 366 488 def get_manipulator_field_objs(self):489 return [oldforms.CheckboxField]490 491 367 def formfield(self, **kwargs): 492 368 defaults = {'form_class': forms.BooleanField} 493 369 defaults.update(kwargs) 494 370 return super(BooleanField, self).formfield(**defaults) 495 371 496 372 class CharField(Field): 497 def get_manipulator_field_objs(self):498 return [oldforms.TextField]499 500 373 def get_internal_type(self): 501 374 return "CharField" 502 375 … … class CharField(Field): 517 390 518 391 # TODO: Maybe move this into contrib, because it's specialized. 519 392 class CommaSeparatedIntegerField(CharField): 520 def get_manipulator_field_objs(self): 521 return [oldforms.CommaSeparatedIntegerField] 393 pass 522 394 523 395 class DateField(Field): 524 396 empty_strings_allowed = False … … class DateField(Field): 562 434 setattr(cls, 'get_previous_by_%s' % self.name, 563 435 curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=False)) 564 436 565 # Needed because of horrible auto_now[_add] behaviour wrt. editable566 def get_follow(self, override=None):567 if override != None:568 return override569 else:570 return self.editable or self.auto_now or self.auto_now_add571 572 437 def get_db_prep_lookup(self, lookup_type, value): 573 438 # For "__month" and "__day" lookups, convert the value to a string so 574 439 # the database backend always sees a consistent type. … … class DateField(Field): 580 445 # Casts dates into the format expected by the backend 581 446 return connection.ops.value_to_db_date(self.to_python(value)) 582 447 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): 587 449 val = self._get_val_from_obj(obj) 588 450 if val is None: 589 451 data = '' 590 452 else: 591 453 data = datetime_safe.new_date(val).strftime("%Y-%m-%d") 592 return {self.attname: data}454 return data 593 455 594 456 def formfield(self, **kwargs): 595 457 defaults = {'form_class': forms.DateField} … … class DateTimeField(DateField): 639 501 # Casts dates into the format expected by the backend 640 502 return connection.ops.value_to_db_datetime(self.to_python(value)) 641 503 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): 661 505 val = self._get_val_from_obj(obj) 662 date_field, time_field = self.get_manipulator_field_names('')663 506 if val is None: 664 dat e_data = time_data = ''507 data = '' 665 508 else: 666 509 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 670 512 671 513 def formfield(self, **kwargs): 672 514 defaults = {'form_class': forms.DateTimeField} … … class DecimalField(Field): 715 557 return connection.ops.value_to_db_decimal(self.to_python(value), 716 558 self.max_digits, self.decimal_places) 717 559 718 def get_manipulator_field_objs(self):719 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]720 721 560 def formfield(self, **kwargs): 722 561 defaults = { 723 562 'max_digits': self.max_digits, … … class EmailField(CharField): 732 571 kwargs['max_length'] = kwargs.get('max_length', 75) 733 572 CharField.__init__(self, *args, **kwargs) 734 573 735 def get_manipulator_field_objs(self):736 return [oldforms.EmailField]737 738 574 def formfield(self, **kwargs): 739 575 defaults = {'form_class': forms.EmailField} 740 576 defaults.update(kwargs) … … class FilePathField(Field): 756 592 defaults.update(kwargs) 757 593 return super(FilePathField, self).formfield(**defaults) 758 594 759 def get_manipulator_field_objs(self):760 return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]761 762 595 def get_internal_type(self): 763 596 return "FilePathField" 764 597 … … class FloatField(Field): 770 603 return None 771 604 return float(value) 772 605 773 def get_manipulator_field_objs(self):774 return [oldforms.FloatField]775 776 606 def get_internal_type(self): 777 607 return "FloatField" 778 608 … … class IntegerField(Field): 788 618 return None 789 619 return int(value) 790 620 791 def get_manipulator_field_objs(self):792 return [oldforms.IntegerField]793 794 621 def get_internal_type(self): 795 622 return "IntegerField" 796 623 … … class IPAddressField(Field): 813 640 kwargs['max_length'] = 15 814 641 Field.__init__(self, *args, **kwargs) 815 642 816 def get_manipulator_field_objs(self):817 return [oldforms.IPAddressField]818 819 643 def get_internal_type(self): 820 644 return "IPAddressField" 821 645 … … class NullBooleanField(Field): 845 669 return None 846 670 return bool(value) 847 671 848 def get_manipulator_field_objs(self):849 return [oldforms.NullBooleanField]850 851 672 def formfield(self, **kwargs): 852 673 defaults = { 853 674 'form_class': forms.NullBooleanField, … … class NullBooleanField(Field): 858 679 return super(NullBooleanField, self).formfield(**defaults) 859 680 860 681 class PhoneNumberField(Field): 861 def get_manipulator_field_objs(self):862 return [oldforms.PhoneNumberField]863 864 682 def get_internal_type(self): 865 683 return "PhoneNumberField" 866 684 … … class PhoneNumberField(Field): 871 689 return super(PhoneNumberField, self).formfield(**defaults) 872 690 873 691 class PositiveIntegerField(IntegerField): 874 def get_manipulator_field_objs(self):875 return [oldforms.PositiveIntegerField]876 877 692 def get_internal_type(self): 878 693 return "PositiveIntegerField" 879 694 … … class PositiveIntegerField(IntegerField): 883 698 return super(PositiveIntegerField, self).formfield(**defaults) 884 699 885 700 class PositiveSmallIntegerField(IntegerField): 886 def get_manipulator_field_objs(self):887 return [oldforms.PositiveSmallIntegerField]888 889 701 def get_internal_type(self): 890 702 return "PositiveSmallIntegerField" 891 703 … … class PositiveSmallIntegerField(IntegerField): 897 709 class SlugField(CharField): 898 710 def __init__(self, *args, **kwargs): 899 711 kwargs['max_length'] = kwargs.get('max_length', 50) 900 kwargs.setdefault('validator_list', []).append(validators.isSlug)901 712 # Set db_index=True unless it's been set manually. 902 713 if 'db_index' not in kwargs: 903 714 kwargs['db_index'] = True … … class SlugField(CharField): 914 725 return super(SlugField, self).formfield(**defaults) 915 726 916 727 class SmallIntegerField(IntegerField): 917 def get_manipulator_field_objs(self):918 return [oldforms.SmallIntegerField]919 920 728 def get_internal_type(self): 921 729 return "SmallIntegerField" 922 730 923 731 class TextField(Field): 924 def get_manipulator_field_objs(self):925 return [oldforms.LargeTextField]926 927 732 def get_internal_type(self): 928 733 return "TextField" 929 734 … … class TimeField(Field): 984 789 # Casts times into the format expected by the backend 985 790 return connection.ops.value_to_db_time(self.to_python(value)) 986 791 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): 991 793 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 993 799 994 800 def formfield(self, **kwargs): 995 801 defaults = {'form_class': forms.TimeField} … … class TimeField(Field): 999 805 class URLField(CharField): 1000 806 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 1001 807 kwargs['max_length'] = kwargs.get('max_length', 200) 1002 if verify_exists:1003 kwargs.setdefault('validator_list', []).append(validators.isExistingURL)1004 808 self.verify_exists = verify_exists 1005 809 CharField.__init__(self, verbose_name, name, **kwargs) 1006 810 1007 def get_manipulator_field_objs(self):1008 return [oldforms.URLField]1009 1010 811 def formfield(self, **kwargs): 1011 812 defaults = {'form_class': forms.URLField, 'verify_exists': self.verify_exists} 1012 813 defaults.update(kwargs) 1013 814 return super(URLField, self).formfield(**defaults) 1014 815 1015 816 class USStateField(Field): 1016 def get_manipulator_field_objs(self):1017 return [oldforms.USStateField]1018 1019 817 def get_internal_type(self): 1020 818 return "USStateField" 1021 819 … … class XMLField(TextField): 1029 827 def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs): 1030 828 self.schema_path = schema_path 1031 829 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 11 11 from django.db.models import signals 12 12 from django.utils.encoding import force_unicode, smart_str 13 13 from django.utils.translation import ugettext_lazy, ugettext as _ 14 from django import oldforms15 14 from django import forms 16 15 from django.core import validators 17 16 from django.db.models.loading import cache … … class FileField(Field): 126 125 attr_class = FieldFile 127 126 128 127 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'): 130 129 if arg in kwargs: 131 130 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__)) 132 131 … … class FileField(Field): 153 152 return None 154 153 return unicode(value) 155 154 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_names164 self.other_file_field_name = other_file_field_name165 self.always_test = True166 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 = True181 field_list[0].validator_list.append(v)182 field_list[0].is_required = field_list[1].is_required = False183 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_list191 192 155 def contribute_to_class(self, cls, name): 193 156 super(FileField, self).contribute_to_class(cls, name) 194 157 setattr(cls, self.name, FileDescriptor(self)) … … class FileField(Field): 206 169 # Otherwise, just close the file, so it doesn't tie up resources. 207 170 file.close() 208 171 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' 217 175 if new_data.get(upload_field_name, False): 218 176 if rel: 219 177 file = new_data[upload_field_name][0] … … class ImageField(FileField): 282 240 self.width_field, self.height_field = width_field, height_field 283 241 FileField.__init__(self, verbose_name, name, **kwargs) 284 242 285 def get_manipulator_field_objs(self):286 return [oldforms.ImageUploadField, oldforms.HiddenField]287 288 243 def formfield(self, **kwargs): 289 244 defaults = {'form_class': forms.ImageField} 290 245 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 4 4 from django.db.models.related import RelatedObject 5 5 from django.db.models.query import QuerySet 6 6 from django.db.models.query_utils import QueryWrapper 7 from django.utils.encoding import smart_unicode 7 8 from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ 8 9 from django.utils.functional import curry 9 10 from django.core import validators 10 from django import oldforms11 11 from django import forms 12 12 13 13 try: … … try: 15 15 except NameError: 16 16 from sets import Set as set # Python 2.3 fallback 17 17 18 # Values for Relation.edit_inline.19 TABULAR, STACKED = 1, 220 21 18 RECURSIVE_RELATIONSHIP_CONSTANT = 'self' 22 19 23 20 pending_lookups = {} … … def do_pending_lookups(sender, **kwargs): 83 80 84 81 signals.class_prepared.connect(do_pending_lookups) 85 82 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.to89 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_name93 94 83 #HACK 95 84 class RelatedField(object): 96 85 def contribute_to_class(self, cls, name): … … class ReverseManyRelatedObjectsDescriptor(object): 580 569 manager.add(*value) 581 570 582 571 class 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): 587 574 try: 588 575 to._meta 589 576 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT 590 577 assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT 591 578 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 595 580 if limit_choices_to is None: 596 581 limit_choices_to = {} 597 582 self.limit_choices_to = limit_choices_to … … class ManyToOneRel(object): 611 596 return data[0] 612 597 613 598 class 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): 618 601 # NOTE: *_num_in_admin and num_extra_on_change are intentionally 619 602 # ignored here. We accept them as parameters only to match the calling 620 603 # 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, 624 606 lookup_overrides=lookup_overrides, parent_link=parent_link) 625 607 self.multiple = False 626 608 627 609 class 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): 630 612 self.to = to 631 self.num_in_admin = num_in_admin632 613 self.related_name = related_name 633 614 if limit_choices_to is None: 634 615 limit_choices_to = {} 635 616 self.limit_choices_to = limit_choices_to 636 self.edit_inline = False637 617 self.symmetrical = symmetrical 638 618 self.multiple = True 639 619 self.through = through … … class ForeignKey(RelatedField, Field): 651 631 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 652 632 653 633 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),659 634 related_name=kwargs.pop('related_name', None), 660 635 limit_choices_to=kwargs.pop('limit_choices_to', None), 661 636 lookup_overrides=kwargs.pop('lookup_overrides', None), … … class ForeignKey(RelatedField, Field): 670 645 def get_validator_unique_lookup_type(self): 671 646 return '%s__%s__exact' % (self.name, self.rel.get_related_field().name) 672 647 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, params681 682 648 def get_default(self): 683 649 "Here we check if the default value is an object and return the to_field if so." 684 650 field_default = super(ForeignKey, self).get_default() … … class ForeignKey(RelatedField, Field): 686 652 return getattr(field_default, self.rel.get_related_field().attname) 687 653 return field_default 688 654 689 def get_manipulator_field_objs(self):690 rel_field = self.rel.get_related_field()691 return [oldforms.IntegerField]692 693 655 def get_db_prep_save(self, value): 694 656 if value == '' or value == None: 695 657 return None 696 658 else: 697 659 return self.rel.get_related_field().get_db_prep_save(value) 698 660 699 def flatten_data(self, follow, obj=None):661 def get_string_value(self, obj): 700 662 if not obj: 701 663 # In required many-to-one fields with only one available choice, 702 664 # select that one available choice. Note: For SelectFields … … class ForeignKey(RelatedField, Field): 705 667 if not self.blank and self.choices: 706 668 choice_list = self.get_choices_default() 707 669 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) 710 672 711 673 def contribute_to_class(self, cls, name): 712 674 super(ForeignKey, self).contribute_to_class(cls, name) … … class OneToOneField(ForeignKey): 744 706 """ 745 707 def __init__(self, to, to_field=None, **kwargs): 746 708 kwargs['unique'] = True 747 if 'num_in_admin' not in kwargs:748 kwargs['num_in_admin'] = 0749 709 super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs) 750 710 751 711 def contribute_to_related_class(self, cls, related): … … class ManyToManyField(RelatedField, Field): 768 728 769 729 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 770 730 kwargs['rel'] = ManyToManyRel(to, 771 num_in_admin=kwargs.pop('num_in_admin', 0),772 731 related_name=kwargs.pop('related_name', None), 773 732 limit_choices_to=kwargs.pop('limit_choices_to', None), 774 733 symmetrical=kwargs.pop('symmetrical', True), … … class ManyToManyField(RelatedField, Field): 786 745 msg = ugettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') 787 746 self.help_text = string_concat(self.help_text, ' ', msg) 788 747 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 793 748 def get_choices_default(self): 794 749 return Field.get_choices(self, include_blank=False) 795 750 … … class ManyToManyField(RelatedField, Field): 869 824 'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 870 825 } 871 826 872 def flatten_data(self, follow, obj = None):873 new_data = {}827 def get_string_value(self, obj): 828 data = '' 874 829 if obj: 875 instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]876 new_data[self.name] = instance_ids830 qs = getattr(obj, self.name).all() 831 data = [instance._get_pk_val() for instance in qs] 877 832 else: 878 833 # In required many-to-many fields with only one available choice, 879 834 # select that one available choice. 880 if not self.blank and not self.rel.edit_inline:835 if not self.blank: 881 836 choices_list = self.get_choices_default() 882 837 if len(choices_list) == 1: 883 new_data[self.name]= [choices_list[0][0]]884 return new_data838 data = [choices_list[0][0]] 839 return smart_unicode(data) 885 840 886 841 def contribute_to_class(self, cls, name): 887 842 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 ObjectDoesNotExist2 from django import oldforms3 from django.core import validators4 from django.db.models.fields import AutoField5 from django.db.models.fields.files import FileField6 from django.db.models import signals7 from django.utils.functional import curry8 from django.utils.datastructures import DotExpandedDict9 from django.utils.text import capfirst10 from django.utils.encoding import smart_str11 from django.utils.translation import ugettext as _12 from django.utils import datetime_safe13 14 def add_manipulators(sender, **kwargs):15 cls = sender16 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 model23 # manipulators (AddManipulator and ChangeManipulator) available via the24 # model class.25 def __init__(self, name, base):26 self.man = None # Cache of the manipulator class.27 self.name = name28 self.base = base29 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" class36 # given in the model class (if specified) and the automatic37 # manipulator.38 bases = [self.base]39 if hasattr(model, 'Manipulator'):40 bases = [model.Manipulator] + bases41 self.man = type(self.name, tuple(bases), {})42 self.man._prepare(model)43 return self.man44 45 class AutomaticManipulator(oldforms.Manipulator):46 def _prepare(cls, model):47 cls.model = model48 cls.manager = model._default_manager49 cls.opts = model._meta50 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] = param97 98 if self.change:99 params[self.opts.pk.attname] = self.obj_key100 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 object110 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 continue145 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 would157 # have caught that.158 all_cores_given, all_cores_blank = True, True159 160 # Get a reference to the old object. We'll use it to compare the161 # old to the new, to see which fields have changed.162 old_rel_obj = None163 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 pass169 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 = False173 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 = False175 # If this field isn't editable, give it the same value it had176 # previously, according to the given ID. If the ID wasn't177 # given, use a default value. FileFields are also a special178 # 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 = None184 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] = param193 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 and225 # 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_object236 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 None243 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_data247 248 class AutomaticAddManipulator(AutomaticManipulator):249 change = False250 251 class AutomaticChangeManipulator(AutomaticManipulator):252 change = True253 def __init__(self, obj_key, follow=None):254 self.obj_key = obj_key255 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 a259 # 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't261 # 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_to267 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_key271 self.original_object = self.opts.get_model_module().Klass(**params)272 else:273 raise274 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 ManyToOneRel278 from django.utils.text import get_text_list279 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 different286 # 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 field291 # doesn't have blank=True.292 return293 if isinstance(f.rel, ManyToOneRel):294 kwargs['%s__pk' % f.name] = field_val295 else:296 kwargs['%s__iexact' % f.name] = field_val297 try:298 old_obj = self.manager.get(**kwargs)299 except ObjectDoesNotExist:300 return301 if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val():302 pass303 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 ManyToOneRel309 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_data316 else:317 lookup_kwargs['%s__iexact' % from_field.name] = field_data318 if lookup_type in ('month', 'date'):319 lookup_kwargs['%s__month' % date_field.name] = date_val.month320 if lookup_type == 'date':321 lookup_kwargs['%s__day' % date_field.name] = date_val.day322 try:323 old_obj = self.manager.get(**lookup_kwargs)324 except ObjectDoesNotExist:325 return326 else:327 if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val():328 pass329 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): 396 396 self._related_many_to_many_cache = cache 397 397 return cache 398 398 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 = None416 fol = f.get_follow(child_override)417 if fol != None:418 follow[f.name] = fol419 return follow420 421 399 def get_base_chain(self, model): 422 400 """ 423 401 Returns a list of parent classes leading to 'model' (order from closet … … class Options(object): 459 437 # objects.append(opts) 460 438 self._ordered_objects = objects 461 439 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 given466 field_type (e.g. FileField).467 """468 # TODO: follow469 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 StopIteration477 # 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 StopIteration482 except StopIteration:483 self._field_types[field_type] = True484 else:485 self._field_types[field_type] = False486 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): 15 15 self.model = model 16 16 self.opts = model._meta 17 17 self.field = field 18 self.edit_inline = field.rel.edit_inline19 18 self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name) 20 19 self.var_name = self.opts.object_name.lower() 21 20 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)] = value34 new_data.update(instance_data)35 return new_data36 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 # TODO43 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 objects50 # corresponding to the xxx_num_in_admin options of the field51 objects = list(attr.all())52 53 count = len(objects) + self.field.rel.num_extra_on_change54 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] * change62 if change < 0:63 return objects[:change]64 else: # Just right65 return objects66 else:67 # A one-to-one relationship, so just return the single related68 # object69 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_admin75 76 21 def get_db_prep_lookup(self, lookup_type, value): 77 22 # Defer to the actual field definition for db prep 78 23 return self.field.get_db_prep_lookup(lookup_type, value) 79 24 80 25 def editable_fields(self): 81 26 "Get the fields in this class that should be edited inline." 82 27 return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] 83 28 84 def get_follow(self, override=None):85 if isinstance(override, bool):86 if override:87 over = {}88 else:89 return None90 else:91 if override:92 over = override.copy()93 elif self.edit_inline:94 over = {}95 else:96 return None97 98 over[self.field.name] = False99 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_change107 else:108 count = self.field.rel.num_in_admin109 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 = 1115 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 fields124 125 29 def __repr__(self): 126 30 return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) 127 31 -
django/forms/fields.py
diff --git a/django/forms/fields.py b/django/forms/fields.py index ee9b8c6..afbf614 100644
a b __all__ = ( 38 38 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 39 39 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 40 40 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 41 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 41 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 42 42 ) 43 43 44 44 # These values, if given to to_python(), will trigger the self.required check. … … class IPAddressField(RegexField): 835 835 836 836 def __init__(self, *args, **kwargs): 837 837 super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs) 838 839 slug_re = re.compile(r'^[-\w]+$') 840 841 class 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 validators2 from django.core.exceptions import PermissionDenied3 from django.utils.html import escape4 from django.utils.safestring import mark_safe5 from django.conf import settings6 from django.utils.translation import ugettext, ungettext7 from django.utils.encoding import smart_unicode, force_unicode8 9 FORM_FIELD_ID_PREFIX = 'id_'10 11 class EmptyValue(Exception):12 "This is raised when empty data is provided"13 pass14 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 objects22 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 field29 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 return37 raise KeyError, "Field %s not found" % field_name38 39 def check_permissions(self, user):40 """Confirms user has required permissions to use this manipulator; raises41 PermissionDenied on failure."""42 if self.required_permission is None:43 return44 if user.has_perm(self.required_permission):45 return46 raise PermissionDenied47 48 def prepare(self, new_data):49 """50 Makes any necessary preparations to new_data, in place, before data has51 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_name63 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 # continue73 # try:74 # validator_list = field.validator_list75 # 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 ValidationErrors87 # # for this particular field88 # except validators.CriticalValidationError, e:89 # errors.setdefault(field.field_name, []).extend(e.messages)90 return errors91 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_name95 raise NotImplementedError96 97 def do_html2python(self, new_data):98 """99 Convert the data from HTML data types to Python datatypes, changing the100 object in place. This happens after validation but before storage. This101 must happen after validation because html2python functions aren't102 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 feeding111 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 = manipulator115 if data is None:116 data = {}117 if error_dict is None:118 error_dict = {}119 self.data = data120 self.error_dict = error_dict121 self._inline_collections = None122 self.edit_inline = edit_inline123 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 compatibility136 # with hand-crafted forms.137 if inline_collection.name == key or (':' not in key and inline_collection.orig_name == key):138 return inline_collection139 raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key140 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 = ic150 151 def has_errors(self):152 return self.error_dict != {}153 154 def _get_fields(self):155 try:156 return self._fields157 except AttributeError:158 self._fields = [self.__getitem__(field.field_name) for field in self.manipulator.fields]159 return self._fields160 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_list167 self.field_name = self.formfield.field_name # for convenience in templates168 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_name179 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_list189 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_dict203 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_dict216 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 errors224 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_manipulator235 self.rel_obj = rel_obj236 self.data = data237 self.errors = errors238 self._collections = None239 self.name = rel_obj.name240 # This is the name used prior to fixing #1839. Needs for backwards241 # compatibility.242 self.orig_name = rel_obj.opts.module_name243 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 return271 else:272 var_name = self.rel_obj.opts.object_name.lower()273 collections = {}274 orig = None275 if hasattr(self.parent_manipulator, 'original_object'):276 orig = self.parent_manipulator.original_object277 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 = collections290 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_name297 The field's name for use by programs.298 validator_list299 A list of validation tests (callback functions) that the data for300 this field must pass in order to be added or changed.301 is_required302 A Boolean. Is it a required field?303 Subclasses should also implement a render(data) method, which is responsible304 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_name315 316 def prepare(self, new_data):317 "Hook for doing something to new_data (in place) before validation."318 pass319 320 def html2python(data):321 "Hook for converting an HTML datatype (e.g. 'on' for checkboxes) to a Python type"322 return data323 html2python = staticmethod(html2python)324 325 def render(self, data):326 raise NotImplementedError327 328 def get_member_name(self):329 if hasattr(self, 'member_name'):330 return self.member_name331 else:332 return self.field_name333 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 data342 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 = d351 new_data.setlist(name, converted_data)352 else:353 try:354 #individual fields deal with None values themselves355 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 errors372 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 ValidationErrors379 # for this particular field380 except validators.CriticalValidationError, e:381 errors.setdefault(self.field_name, []).extend(e.messages)382 return errors383 384 def get_id(self):385 "Returns the HTML 'id' attribute for this form field."386 return FORM_FIELD_ID_PREFIX + self.field_name387 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_name397 self.length, self.max_length = length, max_length398 self.is_required = is_required399 self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list400 if member_name != None:401 self.member_name = member_name402 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_length407 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_length418 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 data424 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_name433 self.rows, self.cols, self.is_required = rows, cols, is_required434 self.validator_list = validator_list[:]435 if max_length:436 self.validator_list.append(self.isValidLength)437 self.max_length = max_length438 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_required450 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_name460 self.checked_by_default = checked_by_default461 self.is_required = is_required462 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 True476 return False477 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_name485 # choices is a list of (value, human-readable key) tuples because order matters486 self.choices, self.size, self.is_required = choices, size, is_required487 self.validator_list = [self.isValidChoice] + validator_list488 if member_name != None:489 self.member_name = member_name490 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 string496 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 None515 return data516 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_name524 # choices is a list of (value, human-readable key) tuples because order matters525 self.choices, self.is_required = choices, is_required526 self.validator_list = [self.isValidChoice] + validator_list527 self.ul_class = ul_class528 if member_name != None:529 self.member_name = member_name530 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 default537 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_class551 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 d560 def __len__(self):561 return len(self.datalist)562 datalist = []563 str_data = smart_unicode(data) # normalize to string564 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 = True603 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 strings608 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 EmptyValue626 return data627 html2python = staticmethod(html2python)628 629 class CheckboxSelectMultipleField(SelectMultipleField):630 """631 This has an identical interface to SelectMultipleField, except the rendered632 widget is different. Instead of a <select multiple>, this widget outputs a633 <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 elements637 back into the single list that validators, renderers and save() expect.638 """639 requires_data_list = True640 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_class644 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 it648 # 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 strings658 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_required677 self.validator_list = [self.isNonEmptyFile] + validator_list678 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_errors684 try:685 file_size = new_data.size686 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 EmptyValue703 return data704 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.messages717 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_list726 if member_name is not None:727 self.member_name = member_name728 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.messages735 736 def html2python(data):737 if data == '' or data is None:738 return None739 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_list746 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_list756 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_list766 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_list776 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 None781 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_places788 validator_list = [self.isValidDecimal] + validator_list789 # 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.messages798 799 def html2python(data):800 if data == '' or data is None:801 return None802 try:803 import decimal804 except ImportError:805 from django.utils import _decimal as decimal806 try:807 return decimal.Decimal(data)808 except decimal.InvalidOperation, e:809 raise ValueError, e810 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_name822 self.length, self.max_length = length, max_length823 self.is_required = is_required824 self.validator_list = [validators.isValidANSIDatetime] + validator_list825 826 def html2python(data):827 "Converts the field into a datetime.datetime object"828 import datetime829 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 = 0838 return datetime.datetime(int(y), int(m), int(d), int(h), int(mn), s)839 except ValueError:840 return None841 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_list849 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.messages857 858 def html2python(data):859 "Converts the field into a datetime.date object"860 import time, datetime861 try:862 time_tuple = time.strptime(data, '%Y-%m-%d')863 return datetime.date(*time_tuple[0:3])864 except (ValueError, TypeError):865 return None866 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_list874 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.messages882 883 def html2python(data):884 "Converts the field into a datetime.time object"885 import time, datetime886 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 provided891 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 t896 except (ValueError, TypeError, AttributeError):897 return None898 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_list909 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.messages917 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_list923 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.messages931 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_list936 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.messages944 945 def html2python(data):946 return data or None947 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 os957 from django.db.models import BLANK_CHOICE_DASH958 if match is not None:959 import re960 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 pass976 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_list983 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.messages991 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_list997 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.messages1005 1006 def html2python(data):1007 if data:1008 return data.upper() # Should always be stored in upper case1009 return data1010 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_list1017 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.messages1025 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 the1044 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_path1048 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? 108 108 All of Django's fields (and when we say *fields* in this document, we always 109 109 mean model fields and not :ref:`form fields <ref-forms-fields>`) are subclasses 110 110 of :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 theclass behavior.111 about a field is common to all fields -- name, help text, uniqueness and so 112 forth. Storing all that information is handled by ``Field``. We'll get into the 113 precise details of what ``Field`` can do later on; for now, suffice it to say 114 that everything descends from ``Field`` and then customizes key pieces of the 115 class behavior. 116 116 117 117 It's important to realize that a Django field class is not what is stored in 118 118 your model attributes. The model attributes contain normal Python objects. The … … parameters: 210 210 * :attr:`~django.db.models.Field.unique_for_date` 211 211 * :attr:`~django.db.models.Field.unique_for_month` 212 212 * :attr:`~django.db.models.Field.unique_for_year` 213 * :attr:`~django.db.models.Field.validator_list`214 213 * :attr:`~django.db.models.Field.choices` 215 214 * :attr:`~django.db.models.Field.help_text` 216 215 * :attr:`~django.db.models.Field.db_column` … … output in some other place, outside of Django. 567 566 Converting field data for serialization 568 567 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 569 568 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) 581 570 582 571 This 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:: 572 output. Calling :meth:``Field._get_val_from_obj(obj)`` is the best way to get the 573 value to serialize. For example, since our ``HandField`` uses strings for its 574 data storage anyway, we can reuse some existing conversion code:: 590 575 591 576 class HandField(models.Field): 592 577 # ... 593 578 594 def flatten_data(self, follow, obj=None):579 def get_string_value(self, obj): 595 580 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) 597 582 598 583 Some general advice 599 584 -------------------- -
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 manipulators5 ===============================6 7 Forwards-compatibility note8 ===========================9 10 The legacy forms/manipulators system described in this document is going to be11 replaced in the next Django release. If you're starting from scratch, we12 strongly encourage you not to waste your time learning this. Instead, learn and13 use the new :ref:`forms library <topics-forms-index>`.14 15 Introduction16 ============17 18 Once you've got a chance to play with Django's admin interface, you'll probably19 wonder if the fantastic form validation framework it uses is available to user20 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. Throughout24 this document, we'll be working with the following model, a "place" object::25 26 from django.db import models27 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 pass45 46 def __unicode__(self):47 return self.name48 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 Manipulators53 ======================54 55 The highest-level interface for object creation and modification is the56 **automatic Manipulator** framework. An automatic manipulator is a utility57 class tied to a given model that "knows" how to create or modify instances of58 that model and how to validate data for the object. Automatic Manipulators come59 in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally60 they are quite similar, but the former knows how to create new instances of the61 model, while the latter modifies existing instances. Both types of classes are62 automatically created when you define a new class::63 64 >>> from mysite.myapp.models import Place65 >>> Place.AddManipulator66 <class 'django.models.manipulators.AddManipulator'>67 >>> Place.ChangeManipulator68 <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 takes74 POSTed data from the browser and creates a new ``Place`` object::75 76 from django.shortcuts import render_to_response77 from django.http import Http404, HttpResponse, HttpResponseRedirect78 from django import oldforms as forms79 from mysite.myapp.models import Place80 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 can87 # 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 the91 # 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, this101 view has a number of problems:102 103 * No validation of any sort is performed. If, for example, the ``name`` field104 isn't given in ``request.POST``, the save step will cause a database error105 because that field is required. Ugly.106 107 * Even if you *do* perform validation, there's still no way to give that108 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 this111 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 a114 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. Ignore119 # 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 the124 first example above.)125 126 The ``forms.FormWrapper`` object is a wrapper that templates can127 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 over147 some salient points of the above template:148 149 * Field "widgets" are handled for you: ``{{ form.field }}`` automatically150 creates the "right" type of widget for the form, as you can see with the151 ``place_type`` field above.152 153 * There isn't a way just to spit out the form. You'll still need to define154 how the form gets laid out. This is a feature: Every form should be155 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 can157 probably find better HTML than in the above template.158 159 * To avoid name conflicts, the ``id`` values of form elements take the160 form "id_*fieldname*".161 162 By creating a creation form we've solved problem number 3 above, but we still163 don't have any validation. Let's revise the validation issue by writing a new164 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 errors171 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 presented181 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 the199 submission form.200 201 * Errors, though nicely presented, are on a separate page, so the user will202 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 form205 and the submission -- into a single view. This view will be responsible for206 creating the form, validating POSTed data, and creating the new object (if the207 data is valid). An added bonus of this approach is that errors and the form will208 both be available on the same page, so errors with fields can be presented in209 context.210 211 .. admonition:: Philosophy:212 213 Finally, for the HTTP purists in the audience (and the authorship), this214 nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches215 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 redirect235 # after POST data, so that reloads don't accidentally create236 # duplicate entries, and so users don't see the confusing237 # "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. Pulling291 the data from ``request.POST``, as is done above, makes sure that if there are292 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 from296 ``manipulator.get_validation_errors``. When passed into the ``FormWrapper``,297 this gives each field an ``errors`` item (which is a list of error messages298 associated with the field) as well as a ``html_error_list`` item, which is a299 ``<ul>`` of error messages. The above template uses these error items to300 display a simple error message next to each field. The error list is saved as301 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. What307 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 a311 # ChangeManipulator at the same time.312 try:313 manipulator = Place.ChangeManipulator(place_id)314 except Place.DoesNotExist:315 raise Http404316 317 # Grab the Place object in question for future use.318 place = manipulator.original_object319 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 object341 to be changed. As you can see, the initializer will raise an342 ``ObjectDoesNotExist`` exception if the ID is invalid.343 344 * ``ChangeManipulator.original_object`` stores the instance of the345 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 under349 manipulation, and converts it into a data dictionary that can be used350 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 be353 "skinned" differently if needed, but the form chunk itself is completely354 identical to the one in the create form above.355 356 The astute programmer will notice the add and create functions are nearly357 identical and could in fact be collapsed into a single view. This is left as an358 exercise for said programmer.359 360 (However, the even-more-astute programmer will take heed of the note at the top361 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 manipulators365 =============================366 367 All the above is fine and dandy if you just want to use the automatically368 created manipulators. But the coolness doesn't end there: You can easily create369 your own custom manipulators for handling custom forms.370 371 Custom manipulators are pretty simple. Here's a manipulator that you might use372 for a "contact" form on a website::373 374 from django import oldforms as forms375 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 required393 method of a custom manipulator is ``__init__`` which must define the fields394 present in the manipulator. See the ``django.forms`` module for395 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 manipulators417 ------------------------------------------------------418 419 It is possible (although rarely needed) to replace the default automatically420 created manipulators on a model with your own custom manipulators. If you do421 this and you are intending to use those models in generic views, you should422 also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement.423 This should act like the default ``flatten_data`` and return a dictionary424 mapping field names to their values, like so::425 426 def flatten_data(self):427 obj = self.original_object428 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 default435 version.436 437 ``FileField`` and ``ImageField`` special cases438 ==============================================439 440 Dealing with ``FileField`` and ``ImageField`` objects is a little more441 complicated.442 443 First, you'll need to make sure that your ``<form>`` element correctly defines444 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. A449 ``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 upload461 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 than465 ``request.POST``, for the uploaded files. This is necessary because466 ``request.POST`` does not contain file-upload data.467 468 For example, following the ``new_data`` convention, you might do something like469 this::470 471 new_data = request.POST.copy()472 new_data.update(request.FILES)473 474 Validators475 ==========476 477 One useful feature of manipulators is the automatic validation. Validation is478 done using a simple validation API: A validator is a callable that raises a479 ``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 validators484 from django import oldforms as forms485 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 to499 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 all503 the data being validated.504 505 .. admonition:: Note::506 507 At the point validators are called all data will still be508 strings (as ``do_html2python`` hasn't been called yet).509 510 Also, because consistency in user interfaces is important, we strongly urge you511 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, all519 the validators for that field are called in turn. The emphasized portion in the520 last sentence is important: if a form field is not submitted (because it521 contains no data -- which is normal HTML behavior), the validators are not522 run against the field.523 524 This feature is particularly important for models using525 ``models.BooleanField`` or custom manipulators using things like526 ``forms.CheckBoxField``. If the checkbox is not selected, it will not527 contribute to the form submission.528 529 If you would like your validator to run *always*, regardless of whether its530 attached field contains any data, set the ``always_test`` attribute on the531 validator function. For example::532 533 def my_custom_validator(field_data, all_data):534 # ...535 my_custom_validator.always_test = True536 537 This validator will always be executed for any field it is attached to.538 539 Ready-made validators540 ---------------------541 542 Writing your own validator is not difficult, but there are some situations543 that come up over and over again. Django comes with a number of validators544 that can be used directly in your code. All of these functions and classes545 reside in ``django/core/validators.py``.546 547 The following validators should all be self-explanatory. Each one provides a548 check for the given property:549 550 * isAlphaNumeric551 * isAlphaNumericURL552 * isSlug553 * isLowerCase554 * isUpperCase555 * isCommaSeparatedIntegerList556 * isCommaSeparatedEmailList557 * isValidIPAddress4558 * isNotEmpty559 * isOnlyDigits560 * isNotOnlyDigits561 * isInteger562 * isOnlyLetters563 * isValidANSIDate564 * isValidANSITime565 * isValidEmail566 * isValidFloat567 * isValidImage568 * isValidImageURL569 * isValidPhone570 * isValidQuicktimeVideoURL571 * isValidURL572 * isValidHTML573 * isWellFormedXml574 * isWellFormedXmlFragment575 * isExistingURL576 * isValidUSState577 * hasNoProfanities578 579 There are also a group of validators that are slightly more flexible. For580 these validators, you create a validator instance, passing in the parameters581 described below. The returned object is a callable that can be used as a582 validator.583 584 For example::585 586 from django.core import validators587 from django import oldforms as forms588 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 as599 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 final602 argument (``error_message``) that is the message returned when validation603 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 value607 matches the contents of the other field.608 609 ``ValidateIfOtherFieldEquals``610 Takes three parameters: ``other_field``, ``other_value`` and611 ``validator_list``, in that order. If ``other_field`` has a value of612 ``other_value``, then the validators in ``validator_list`` are all run613 against the current field.614 615 ``RequiredIfOtherFieldGiven``616 Takes a field name of the current field is only required if the other617 field has a value.618 619 ``RequiredIfOtherFieldsGiven``620 Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of621 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 the626 other field has no value.627 628 ``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual``629 Each of these validator classes takes a field name and a value (in that630 order). If the given field does (or does not have, in the latter case) the631 given value, then the current field being validated is required.632 633 An optional ``other_label`` argument can be passed which, if given, is used634 in error messages instead of the value. This allows more user friendly error635 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. So639 ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst640 ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the641 equality test succeeding.642 643 ``IsLessThanOtherField``644 Takes a field name and validates that the current field being validated645 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 using647 this function to compare data that should be treated as another type. The648 string "123" is less than the string "2", for example. If you don't want649 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 the653 field is greater than ``lower`` (if given) and less than ``upper`` (if654 given).655 656 Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow657 values of both 10 and 20. This validator only checks numeric values658 (e.g., float and integer values).659 660 ``IsAPowerOf``661 Takes an integer argument and when called as a validator, checks that the662 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 that666 order) and validates whether the field is a decimal with no more than the667 maximum number of digits and decimal places.668 669 ``MatchesRegularExpression``670 Takes a regular expression (a string) as a parameter and validates the671 field value against it.672 673 ``AnyValidator``674 Takes a list of validators as a parameter. At validation time, if the675 field successfully validates against any one of the validators, it passes676 validation. The validators are tested in the order specified in the677 original list.678 679 ``URLMimeTypeCheck``680 Used to validate URL fields. Takes a list of MIME types (such as681 ``text/plain``) at creation time. At validation time, it verifies that the682 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 content684 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 a688 file path to the location of the schema and an optional root element (which689 is wrapped around the XML fragment before validation, if supplied). At690 validation time, the XML fragment is validated against the schema using the691 executable specified in the ``JING_PATH`` setting (see the :ref:`settings692 <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 new7 forever. We plan to rename it to ``django.forms`` in next official release. The8 current ``django.forms`` package will be available as ``django.oldforms`` until9 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're12 probably not -- you need to read this document and understand this migration13 plan.14 15 * The old forms framework (the current ``django.forms``) has been copied to16 ``django.oldforms``. Thus, you can start upgrading your code *now*,17 rather than waiting for the future backwards-incompatible change, by18 changing your import statements like this::19 20 from django import forms # old21 from django import oldforms as forms # new22 23 * In the next Django release, we will move the current ``django.newforms``24 to ``django.forms``. This will be a backwards-incompatible change, and25 anybody who is still using the old version of ``django.forms`` at that26 time will need to change their import statements, as described in the27 previous bullet.28 29 * We will remove ``django.oldforms`` in Django 1.0. It will continue to be30 available from older tags in our SVN repository, but it will not be31 consider part of Django, and will not be supported..32 33 With this in mind, we recommend you use the following import statement when34 using ``django.newforms``::35 36 from django import newforms as forms37 38 This way, your code can refer to the ``forms`` module, and when39 ``django.newforms`` is renamed to ``django.forms``, you'll only have to change40 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 utilities47 into your local namespace. Some people find this convenient; others find it48 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 145 145 proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is 146 146 meant for static data that doesn't change much, if ever. 147 147 148 ``core``149 --------150 151 .. attribute:: Field.core152 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 are156 cleared, the object will be deleted.157 158 It is an error to have an inline-editable relation without at least one159 ``core=True`` field.160 161 Please note that each field marked "core" is treated as a required field by the162 Django admin site. Essentially, this means you should put ``core=True`` on all163 required fields in your related object that is being edited inline.164 165 148 ``db_column`` 166 149 ------------- 167 150 … … respect to the month. 287 270 288 271 Like :attr:`~Field.unique_for_date` and :attr:`~Field.unique_for_month`. 289 272 290 ``validator_list``291 ------------------292 293 .. attribute:: Field.validator_list294 295 A list of extra validators to apply to the field. Each should be a callable that296 takes the parameters ``field_data, all_data`` and raises297 :exc:`django.core.validators.ValidationError` for errors.298 299 273 .. _model-field-types: 300 274 301 275 Field types … … that control how the relationship functions. 913 887 914 888 The semantics of one-to-one relationships will be changing soon, so we don't 915 889 recommend 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: 67 67 (from ``django.contrib.localflavor.us``) 68 68 ``PositiveIntegerField`` ``IntegerField`` 69 69 ``PositiveSmallIntegerField`` ``IntegerField`` 70 ``SlugField`` ``RegexField`` accepting only letters, 71 numbers, underscores and hyphens 70 ``SlugField`` ``SlugField`` 72 71 ``SmallIntegerField`` ``IntegerField`` 73 72 ``TextField`` ``CharField`` with ``widget=Textarea`` 74 73 ``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: 899 899 rendered on the form. 900 900 901 901 ``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. 904 903 905 904 ``field`` is the name of the field on the form to check. If ``field`` 906 905 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): 53 53 return [] 54 54 raise FieldError('Invalid lookup type: %r' % lookup_type) 55 55 56 def flatten_data(self, follow, obj=None):57 return {self.attname: force_unicode(self._get_val_from_obj(obj))}58 59 56 class MyModel(models.Model): 60 57 name = models.CharField(max_length=10) 61 58 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): 23 23 clash1_set = models.CharField(max_length=10) 24 24 25 25 class Clash1(models.Model): 26 src_safe = models.CharField(max_length=10 , core=True)26 src_safe = models.CharField(max_length=10) 27 27 28 28 foreign = models.ForeignKey(Target) 29 29 m2m = models.ManyToManyField(Target) 30 30 31 31 class Clash2(models.Model): 32 src_safe = models.CharField(max_length=10 , core=True)32 src_safe = models.CharField(max_length=10) 33 33 34 34 foreign_1 = models.ForeignKey(Target, related_name='id') 35 35 foreign_2 = models.ForeignKey(Target, related_name='src_safe') … … class Target2(models.Model): 46 46 clashm2m_set = models.ManyToManyField(Target) 47 47 48 48 class Clash3(models.Model): 49 src_safe = models.CharField(max_length=10 , core=True)49 src_safe = models.CharField(max_length=10) 50 50 51 51 foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt') 52 52 foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt') … … class ClashM2M(models.Model): 61 61 m2m = models.ManyToManyField(Target2) 62 62 63 63 class SelfClashForeign(models.Model): 64 src_safe = models.CharField(max_length=10 , core=True)64 src_safe = models.CharField(max_length=10) 65 65 selfclashforeign = models.CharField(max_length=10) 66 66 67 67 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-82 """3 27. Default manipulators4 5 Each model gets an ``AddManipulator`` and ``ChangeManipulator`` by default.6 """7 8 from django.db import models9 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.name24 25 __test__ = {'API_TESTS':u"""26 >>> from django.utils.datastructures import MultiValueDict27 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 True42 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().musician83 <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 >>> a297 <Album: Ultimate Ella>98 >>> a2.release_date99 datetime.date(2005, 2, 13)100 101 # Test isValidFloat Unicode coercion102 >>> from django.core.validators import isValidFloat, ValidationError103 >>> try: isValidFloat(u"ä", None)104 ... except ValidationError: pass105 """} -
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. 7 7 from django.db.models import * 8 8 9 9 class Parent(Model): 10 name = CharField(max_length=100 , core=True)10 name = CharField(max_length=100) 11 11 12 12 # Use a simple string for forward declarations. 13 13 bestchild = ForeignKey("Child", null=True, related_name="favoured_by")