Ticket #7742: 7742.diff
File 7742.diff, 141.0 KB (added by , 16 years ago) |
---|
-
django/contrib/contenttypes/generic.py
=== modified file 'django/contrib/contenttypes/generic.py'
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 """ … … 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 … … 290 281 """ 291 282 ct_field_name = "content_type" 292 283 ct_fk_field_name = "object_id" 293 284 294 285 def __init__(self, data=None, files=None, instance=None, save_as_new=None): 295 286 opts = self.model._meta 296 287 self.instance = instance … … 385 376 386 377 class GenericTabularInline(GenericInlineModelAdmin): 387 378 template = 'admin/edit_inline/tabular.html' 388 -
django/contrib/gis/db/models/fields/__init__.py
=== modified file 'django/contrib/gis/db/models/fields/__init__.py'
31 31 Indicates whether to create a spatial index. Defaults to True. 32 32 Set this instead of 'db_index' for geographic fields since index 33 33 creation is different for geometry columns. 34 34 35 35 dim: 36 36 The number of dimensions for this geometry. Defaults to 2. 37 37 """ … … 39 39 # Setting the index flag with the value of the `spatial_index` keyword. 40 40 self._index = spatial_index 41 41 42 # Setting the SRID and getting the units. Unit information must be 42 # Setting the SRID and getting the units. Unit information must be 43 43 # easily available in the field instance for distance queries. 44 44 self._srid = srid 45 45 self._unit, self._unit_name, self._spheroid = get_srid_info(srid) 46 46 47 47 # Setting the dimension of the geometry field. 48 48 self._dim = dim 49 50 # Setting the verbose_name keyword argument with the positional 49 50 # Setting the verbose_name keyword argument with the positional 51 51 # first parameter, so this works like normal fields. 52 52 kwargs['verbose_name'] = verbose_name 53 53 54 54 super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function 55 55 56 56 ### Routines specific to GeometryField ### … … 64 64 65 65 def get_distance(self, dist_val, lookup_type): 66 66 """ 67 Returns a distance number in units of the field. For example, if 67 Returns a distance number in units of the field. For example, if 68 68 `D(km=1)` was passed in and the units of the field were in meters, 69 69 then 1000 would be returned. 70 70 """ … … 84 84 else: 85 85 # Assuming the distance is in the units of the field. 86 86 dist_param = dist 87 87 88 88 if SpatialBackend.postgis and self.geodetic and lookup_type != 'dwithin' and option == 'spheroid': 89 # On PostGIS, by default `ST_distance_sphere` is used; but if the 90 # accuracy of `ST_distance_spheroid` is needed than the spheroid 89 # On PostGIS, by default `ST_distance_sphere` is used; but if the 90 # accuracy of `ST_distance_spheroid` is needed than the spheroid 91 91 # needs to be passed to the SQL stored procedure. 92 92 return [gqn(self._spheroid), dist_param] 93 93 else: … … 98 98 Retrieves the geometry, setting the default SRID from the given 99 99 lookup parameters. 100 100 """ 101 if isinstance(value, (tuple, list)): 101 if isinstance(value, (tuple, list)): 102 102 geom = value[0] 103 103 else: 104 104 geom = value … … 117 117 118 118 # Assigning the SRID value. 119 119 geom.srid = self.get_srid(geom) 120 120 121 121 return geom 122 122 123 123 def get_srid(self, geom): … … 135 135 ### Routines overloaded from Field ### 136 136 def contribute_to_class(self, cls, name): 137 137 super(GeometryField, self).contribute_to_class(cls, name) 138 138 139 139 # Setup for lazy-instantiated Geometry object. 140 140 setattr(cls, self.attname, GeometryProxy(SpatialBackend.Geometry, self)) 141 141 142 142 def formfield(self, **kwargs): 143 defaults = {'form_class' : forms.GeometryField, 143 defaults = {'form_class' : forms.GeometryField, 144 144 'geom_type' : self._geom, 145 145 'null' : self.null, 146 146 } … … 161 161 # if it is None. 162 162 geom = self.get_geometry(value) 163 163 164 # Getting the WHERE clause list and the associated params list. The params 165 # list is populated with the Adaptor wrapping the Geometry for the 164 # Getting the WHERE clause list and the associated params list. The params 165 # list is populated with the Adaptor wrapping the Geometry for the 166 166 # backend. The WHERE clause list contains the placeholder for the adaptor 167 167 # (e.g. any transformation SQL). 168 168 where = [self.get_placeholder(geom)] -
django/core/serializers/base.py
=== modified file 'django/core/serializers/base.py'
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/base.py
=== modified file 'django/db/models/base.py'
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 -
django/db/models/fields/__init__.py
=== modified file 'django/db/models/fields/__init__.py'
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 … … 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. … … 75 63 max_length=None, unique=False, blank=False, null=False, 76 64 db_index=False, core=False, rel=None, default=NOT_PROVIDED, 77 65 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,66 unique_for_month=None, 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 … … 90 78 self.core, self.rel, self.default = core, 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 [] … … 274 261 return None 275 262 return "" 276 263 277 def get_manipulator_field_names(self, name_prefix):278 """279 Returns a list of field names that this object adds to the manipulator.280 """281 return [name_prefix + self.name]282 283 def prepare_field_objs_and_params(self, manipulator, name_prefix):284 params = {'validator_list': self.validator_list[:]}285 if self.max_length and not self.choices: # Don't give SelectFields a max_length parameter.286 params['max_length'] = self.max_length287 288 if self.choices:289 field_objs = [oldforms.SelectField]290 291 params['choices'] = self.get_flatchoices()292 else:293 field_objs = self.get_manipulator_field_objs()294 return (field_objs, params)295 296 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):297 """298 Returns a list of oldforms.FormField instances for this field. It299 calculates the choices at runtime, not at compile time.300 301 name_prefix is a prefix to prepend to the "field_name" argument.302 rel is a boolean specifying whether this field is in a related context.303 """304 field_objs, params = self.prepare_field_objs_and_params(manipulator, name_prefix)305 306 # Add the "unique" validator(s).307 for field_name_list in opts.unique_together:308 if field_name_list[0] == self.name:309 params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))310 311 # Add the "unique for..." validator(s).312 if self.unique_for_date:313 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))314 if self.unique_for_month:315 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))316 if self.unique_for_year:317 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))318 if self.unique and not rel:319 params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))320 321 # Only add is_required=True if the field cannot be blank. Primary keys322 # are a special case, and fields in a related context should set this323 # as False, because they'll be caught by a separate validator --324 # RequiredIfOtherFieldGiven.325 params['is_required'] = not self.blank and not self.primary_key and not rel326 327 # BooleanFields (CheckboxFields) are a special case. They don't take328 # is_required.329 if isinstance(self, BooleanField):330 del params['is_required']331 332 # If this field is in a related context, check whether any other fields333 # in the related object have core=True. If so, add a validator --334 # RequiredIfOtherFieldsGiven -- to this FormField.335 if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):336 # First, get the core fields, if any.337 core_field_names = []338 for f in opts.fields:339 if f.core and f != self:340 core_field_names.extend(f.get_manipulator_field_names(name_prefix))341 # Now, if there are any, add the validator to this FormField.342 if core_field_names:343 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, ugettext_lazy("This field is required.")))344 345 # Finally, add the field_names.346 field_names = self.get_manipulator_field_names(name_prefix)347 return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]348 349 264 def get_validator_unique_lookup_type(self): 350 265 return '%s__exact' % self.name 351 266 352 def get_manipulator_new_data(self, new_data, rel=False):353 """354 Given the full new_data dictionary (from the manipulator), returns this355 field's data.356 """357 if rel:358 return new_data.get(self.name, [self.get_default()])[0]359 val = new_data.get(self.name, self.get_default())360 if not self.empty_strings_allowed and val == '' and self.null:361 val = None362 return val363 364 267 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 365 268 """Returns choices with a default blank choices included, for use 366 269 as SelectField choices for this field.""" … … 388 291 else: 389 292 return self.get_default() 390 293 391 def flatten_data(self, follow, obj=None): 392 """ 393 Returns a dictionary mapping the field's manipulator field names to its 394 "flattened" string values for the admin view. obj is the instance to 395 extract the values from. 396 """ 397 return {self.attname: self._get_val_from_obj(obj)} 398 399 def get_follow(self, override=None): 400 if override != None: 401 return override 402 else: 403 return self.editable 294 def get_string_value(self, obj): 295 """ 296 Returns a string value of this field from the passed obj. 297 """ 298 return smart_unicode(self._get_val_from_obj(obj)) 404 299 405 300 def bind(self, fieldmapping, original, bound_field_class): 406 301 return bound_field_class(self, fieldmapping, original) … … 461 356 return None 462 357 return int(value) 463 358 464 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):465 if not rel:466 return [] # Don't add a FormField unless it's in a related context.467 return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)468 469 def get_manipulator_field_objs(self):470 return [oldforms.HiddenField]471 472 def get_manipulator_new_data(self, new_data, rel=False):473 # Never going to be called474 # Not in main change pages475 # ignored in related context476 if not rel:477 return None478 return Field.get_manipulator_new_data(self, new_data, rel)479 480 359 def contribute_to_class(self, cls, name): 481 360 assert not cls._meta.has_auto_field, "A model can't have more than one AutoField." 482 361 super(AutoField, self).contribute_to_class(cls, name) … … 507 386 return None 508 387 return bool(value) 509 388 510 def get_manipulator_field_objs(self):511 return [oldforms.CheckboxField]512 513 389 def formfield(self, **kwargs): 514 390 defaults = {'form_class': forms.BooleanField} 515 391 defaults.update(kwargs) 516 392 return super(BooleanField, self).formfield(**defaults) 517 393 518 394 class CharField(Field): 519 def get_manipulator_field_objs(self):520 return [oldforms.TextField]521 522 395 def get_internal_type(self): 523 396 return "CharField" 524 397 … … 539 412 540 413 # TODO: Maybe move this into contrib, because it's specialized. 541 414 class CommaSeparatedIntegerField(CharField): 542 def get_manipulator_field_objs(self): 543 return [oldforms.CommaSeparatedIntegerField] 415 pass 544 416 545 417 class DateField(Field): 546 418 empty_strings_allowed = False … … 584 456 setattr(cls, 'get_previous_by_%s' % self.name, 585 457 curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=False)) 586 458 587 # Needed because of horrible auto_now[_add] behaviour wrt. editable588 def get_follow(self, override=None):589 if override != None:590 return override591 else:592 return self.editable or self.auto_now or self.auto_now_add593 594 459 def get_db_prep_value(self, value): 595 460 # Casts dates into the format expected by the backend 596 461 return connection.ops.value_to_db_date(self.to_python(value)) 597 462 598 def get_manipulator_field_objs(self): 599 return [oldforms.DateField] 600 601 def flatten_data(self, follow, obj=None): 463 def get_string_value(self, obj): 602 464 val = self._get_val_from_obj(obj) 603 465 if val is None: 604 466 data = '' 605 467 else: 606 468 data = datetime_safe.new_date(val).strftime("%Y-%m-%d") 607 return {self.attname: data}469 return data 608 470 609 471 def formfield(self, **kwargs): 610 472 defaults = {'form_class': forms.DateField} … … 654 516 # Casts dates into the format expected by the backend 655 517 return connection.ops.value_to_db_datetime(self.to_python(value)) 656 518 657 def get_manipulator_field_objs(self): 658 return [oldforms.DateField, oldforms.TimeField] 659 660 def get_manipulator_field_names(self, name_prefix): 661 return [name_prefix + self.name + '_date', name_prefix + self.name + '_time'] 662 663 def get_manipulator_new_data(self, new_data, rel=False): 664 date_field, time_field = self.get_manipulator_field_names('') 665 if rel: 666 d = new_data.get(date_field, [None])[0] 667 t = new_data.get(time_field, [None])[0] 668 else: 669 d = new_data.get(date_field, None) 670 t = new_data.get(time_field, None) 671 if d is not None and t is not None: 672 return datetime.datetime.combine(d, t) 673 return self.get_default() 674 675 def flatten_data(self,follow, obj = None): 519 def get_string_value(self, obj): 676 520 val = self._get_val_from_obj(obj) 677 date_field, time_field = self.get_manipulator_field_names('')678 521 if val is None: 679 dat e_data = time_data = ''522 data = '' 680 523 else: 681 524 d = datetime_safe.new_datetime(val) 682 date_data = d.strftime('%Y-%m-%d') 683 time_data = d.strftime('%H:%M:%S') 684 return {date_field: date_data, time_field: time_data} 525 data = d.strftime('%Y-%m-%d %H:%M:%S') 526 return data 685 527 686 528 def formfield(self, **kwargs): 687 529 defaults = {'form_class': forms.DateTimeField} … … 730 572 return connection.ops.value_to_db_decimal(self.to_python(value), 731 573 self.max_digits, self.decimal_places) 732 574 733 def get_manipulator_field_objs(self):734 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]735 736 575 def formfield(self, **kwargs): 737 576 defaults = { 738 577 'max_digits': self.max_digits, … … 747 586 kwargs['max_length'] = kwargs.get('max_length', 75) 748 587 CharField.__init__(self, *args, **kwargs) 749 588 750 def get_manipulator_field_objs(self):751 return [oldforms.EmailField]752 753 589 def validate(self, field_data, all_data): 754 590 validators.isValidEmail(field_data, all_data) 755 591 … … 774 610 defaults.update(kwargs) 775 611 return super(FilePathField, self).formfield(**defaults) 776 612 777 def get_manipulator_field_objs(self):778 return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]779 780 613 def get_internal_type(self): 781 614 return "FilePathField" 782 615 … … 788 621 return None 789 622 return float(value) 790 623 791 def get_manipulator_field_objs(self):792 return [oldforms.FloatField]793 794 624 def get_internal_type(self): 795 625 return "FloatField" 796 626 … … 806 636 return None 807 637 return int(value) 808 638 809 def get_manipulator_field_objs(self):810 return [oldforms.IntegerField]811 812 639 def get_internal_type(self): 813 640 return "IntegerField" 814 641 … … 823 650 kwargs['max_length'] = 15 824 651 Field.__init__(self, *args, **kwargs) 825 652 826 def get_manipulator_field_objs(self):827 return [oldforms.IPAddressField]828 829 653 def get_internal_type(self): 830 654 return "IPAddressField" 831 655 … … 858 682 return None 859 683 return bool(value) 860 684 861 def get_manipulator_field_objs(self):862 return [oldforms.NullBooleanField]863 864 685 def formfield(self, **kwargs): 865 686 defaults = { 866 687 'form_class': forms.NullBooleanField, … … 871 692 return super(NullBooleanField, self).formfield(**defaults) 872 693 873 694 class PhoneNumberField(Field): 874 def get_manipulator_field_objs(self):875 return [oldforms.PhoneNumberField]876 877 695 def get_internal_type(self): 878 696 return "PhoneNumberField" 879 697 … … 887 705 return super(PhoneNumberField, self).formfield(**defaults) 888 706 889 707 class PositiveIntegerField(IntegerField): 890 def get_manipulator_field_objs(self):891 return [oldforms.PositiveIntegerField]892 893 708 def get_internal_type(self): 894 709 return "PositiveIntegerField" 895 710 … … 899 714 return super(PositiveIntegerField, self).formfield(**defaults) 900 715 901 716 class PositiveSmallIntegerField(IntegerField): 902 def get_manipulator_field_objs(self):903 return [oldforms.PositiveSmallIntegerField]904 905 717 def get_internal_type(self): 906 718 return "PositiveSmallIntegerField" 907 719 … … 913 725 class SlugField(CharField): 914 726 def __init__(self, *args, **kwargs): 915 727 kwargs['max_length'] = kwargs.get('max_length', 50) 916 kwargs.setdefault('validator_list', []).append(validators.isSlug)917 728 # Set db_index=True unless it's been set manually. 918 729 if 'db_index' not in kwargs: 919 730 kwargs['db_index'] = True … … 922 733 def get_internal_type(self): 923 734 return "SlugField" 924 735 736 def formfield(self, **kwargs): 737 defaults = {'form_class': forms.SlugField} 738 defaults.update(kwargs) 739 return super(SlugField, self).formfield(**defaults) 740 925 741 class SmallIntegerField(IntegerField): 926 def get_manipulator_field_objs(self):927 return [oldforms.SmallIntegerField]928 929 742 def get_internal_type(self): 930 743 return "SmallIntegerField" 931 744 932 745 class TextField(Field): 933 def get_manipulator_field_objs(self):934 return [oldforms.LargeTextField]935 936 746 def get_internal_type(self): 937 747 return "TextField" 938 748 … … 993 803 # Casts times into the format expected by the backend 994 804 return connection.ops.value_to_db_time(self.to_python(value)) 995 805 996 def get_manipulator_field_objs(self): 997 return [oldforms.TimeField] 998 999 def flatten_data(self,follow, obj = None): 806 def get_string_value(self, obj): 1000 807 val = self._get_val_from_obj(obj) 1001 return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} 808 if val is None: 809 data = '' 810 else: 811 data = val.strftime("%H:%M:%S") 812 return data 1002 813 1003 814 def formfield(self, **kwargs): 1004 815 defaults = {'form_class': forms.TimeField} … … 1008 819 class URLField(CharField): 1009 820 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 1010 821 kwargs['max_length'] = kwargs.get('max_length', 200) 1011 if verify_exists:1012 kwargs.setdefault('validator_list', []).append(validators.isExistingURL)1013 822 self.verify_exists = verify_exists 1014 823 CharField.__init__(self, verbose_name, name, **kwargs) 1015 824 1016 def get_manipulator_field_objs(self):1017 return [oldforms.URLField]1018 1019 825 def formfield(self, **kwargs): 1020 826 defaults = {'form_class': forms.URLField, 'verify_exists': self.verify_exists} 1021 827 defaults.update(kwargs) 1022 828 return super(URLField, self).formfield(**defaults) 1023 829 1024 830 class USStateField(Field): 1025 def get_manipulator_field_objs(self):1026 return [oldforms.USStateField]1027 1028 831 def get_internal_type(self): 1029 832 return "USStateField" 1030 833 … … 1038 841 def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs): 1039 842 self.schema_path = schema_path 1040 843 Field.__init__(self, verbose_name, name, **kwargs) 1041 1042 def get_manipulator_field_objs(self):1043 return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]1044 -
django/db/models/fields/files.py
=== modified file 'django/db/models/fields/files.py'
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 … … 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)) … … 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] … … 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
=== modified file 'django/db/models/fields/related.py'
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: … … 83 83 84 84 signals.class_prepared.connect(do_pending_lookups) 85 85 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 86 #HACK 95 87 class RelatedField(object): 96 88 def contribute_to_class(self, cls, name): … … 652 644 def get_validator_unique_lookup_type(self): 653 645 return '%s__%s__exact' % (self.name, self.rel.get_related_field().name) 654 646 655 def prepare_field_objs_and_params(self, manipulator, name_prefix):656 params = {'validator_list': self.validator_list[:], 'member_name': name_prefix + self.attname}657 if self.null:658 field_objs = [oldforms.NullSelectField]659 else:660 field_objs = [oldforms.SelectField]661 params['choices'] = self.get_choices_default()662 return field_objs, params663 664 647 def get_default(self): 665 648 "Here we check if the default value is an object and return the to_field if so." 666 649 field_default = super(ForeignKey, self).get_default() … … 668 651 return getattr(field_default, self.rel.get_related_field().attname) 669 652 return field_default 670 653 671 def get_manipulator_field_objs(self):672 rel_field = self.rel.get_related_field()673 return [oldforms.IntegerField]674 675 654 def get_db_prep_save(self, value): 676 655 if value == '' or value == None: 677 656 return None 678 657 else: 679 658 return self.rel.get_related_field().get_db_prep_save(value) 680 659 681 def flatten_data(self, follow, obj=None):660 def get_string_value(self, obj): 682 661 if not obj: 683 662 # In required many-to-one fields with only one available choice, 684 663 # select that one available choice. Note: For SelectFields … … 687 666 if not self.blank and self.choices: 688 667 choice_list = self.get_choices_default() 689 668 if len(choice_list) == 2: 690 return {self.attname: choice_list[1][0]}691 return Field. flatten_data(self, follow, obj)669 return smart_unicode(choice_list[1][0]) 670 return Field.get_string_value(self, obj) 692 671 693 672 def contribute_to_class(self, cls, name): 694 673 super(ForeignKey, self).contribute_to_class(cls, name) … … 759 738 msg = ugettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') 760 739 self.help_text = string_concat(self.help_text, ' ', msg) 761 740 762 def get_manipulator_field_objs(self):763 choices = self.get_choices_default()764 return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]765 766 741 def get_choices_default(self): 767 742 return Field.get_choices(self, include_blank=False) 768 743 … … 842 817 'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 843 818 } 844 819 845 def flatten_data(self, follow, obj = None):846 new_data = {}820 def get_string_value(self, obj): 821 data = '' 847 822 if obj: 848 instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]849 new_data[self.name] = instance_ids823 qs = getattr(obj, self.name).all() 824 data = [instance._get_pk_val() for instance in qs] 850 825 else: 851 826 # In required many-to-many fields with only one available choice, 852 827 # select that one available choice. 853 828 if not self.blank and not self.rel.edit_inline: 854 829 choices_list = self.get_choices_default() 855 830 if len(choices_list) == 1: 856 new_data[self.name]= [choices_list[0][0]]857 return new_data831 data = [choices_list[0][0]] 832 return smart_unicode(data) 858 833 859 834 def contribute_to_class(self, cls, name): 860 835 super(ManyToManyField, self).contribute_to_class(cls, name) -
django/db/models/manipulators.py
=== removed file 'django/db/models/manipulators.py'
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
=== modified file 'django/db/models/options.py'
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 … … 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
=== modified file 'django/db/models/related.py'
19 19 self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name) 20 20 self.var_name = self.opts.object_name.lower() 21 21 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 22 def get_list(self, parent_instance=None): 45 23 "Get the list of this type of object from an instance of the parent class." 46 24 if parent_instance is not None: … … 76 54 def get_db_prep_lookup(self, lookup_type, value): 77 55 # Defer to the actual field definition for db prep 78 56 return self.field.get_db_prep_lookup(lookup_type, value) 79 57 80 58 def editable_fields(self): 81 59 "Get the fields in this class that should be edited inline." 82 60 return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] 83 61 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 62 def __repr__(self): 126 63 return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) 127 64 -
django/forms/fields.py
=== modified file 'django/forms/fields.py'
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. … … 824 824 825 825 def __init__(self, *args, **kwargs): 826 826 super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs) 827 828 slug_re = re.compile(r'^[-\w]+$') 829 830 class SlugField(RegexField): 831 default_error_messages = { 832 'invalid': _(u"This value must contain only letters, numbers," 833 u" underscores or hyphens."), 834 } 835 836 def __init__(self, *args, **kwargs): 837 super(SlugField, self).__init__(slug_re, *args, **kwargs) -
django/oldforms/__init__.py
=== removed directory 'django/oldforms' === removed file 'django/oldforms/__init__.py'
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/custom_model_fields.txt
=== modified file 'docs/custom_model_fields.txt'
105 105 All of Django's fields (and when we say *fields* in this document, we always 106 106 mean model fields and not `form fields`_) are subclasses of 107 107 ``django.db.models.Field``. Most of the information that Django records about a 108 field is common to all fields -- name, help text, validator lists, uniqueness109 and so forth. Storing all that information is handled by ``Field``. We'll get 110 into the precise details of what ``Field`` can do later on; for now, suffice it 111 t o say that everything descends from ``Field`` and then customizes key pieces112 of theclass behavior.108 field is common to all fields -- name, help text, uniqueness and so forth. 109 Storing all that information is handled by ``Field``. We'll get into the 110 precise details of what ``Field`` can do later on; for now, suffice it to say 111 that everything descends from ``Field`` and then customizes key pieces of the 112 class behavior. 113 113 114 114 .. _form fields: ../forms/#fields 115 115 … … 202 202 * ``unique_for_date`` 203 203 * ``unique_for_month`` 204 204 * ``unique_for_year`` 205 * ``validator_list``206 205 * ``choices`` 207 206 * ``help_text`` 208 207 * ``db_column`` … … 546 545 547 546 .. _above: #db-type-self 548 547 549 ``flatten_data(self, follow, obj=None)`` 550 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 551 552 .. admonition:: Subject to change 553 554 Although implementing this method is necessary to allow field 555 serialization, the API might change in the future. 556 557 Returns a dictionary, mapping the field's attribute name to a 558 flattened string version of the data. This method has some internal 559 uses that aren't of interest to use here (mostly having to do with 560 forms). For our purposes, it's sufficient to return a one item 561 dictionary that maps the attribute name to a string. 548 ``get_string_value(self, obj)`` 549 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 562 550 563 551 This method is used by the serializers to convert the field into a string for 564 output. You can ignore the input parameters for serialization purposes, 565 although calling ``Field._get_val_from_obj(obj)`` is the best way to get the 566 value to serialize. 567 568 For example, since our ``HandField`` uses strings for its data storage anyway, 569 we can reuse some existing conversion code:: 552 output. Calling ``Field._get_val_from_obj(obj)`` is the best way to get the 553 value to serialize. For example, since our ``HandField`` uses strings for its 554 data storage anyway, we can reuse some existing conversion code:: 570 555 571 556 class HandField(models.Field): 572 557 # ... 573 558 574 def flatten_data(self, follow, obj=None):559 def get_string_value(self, obj): 575 560 value = self._get_val_from_obj(obj) 576 return {self.attname: self.get_db_prep_value(value)}561 return self.get_db_prep_value(value) 577 562 578 563 Some general advice 579 564 -------------------- -
docs/forms.txt
=== modified file 'docs/forms.txt'
2 2 The forms library 3 3 ================= 4 4 5 ``django.forms`` is Django's form-handling library.6 7 .. admonition:: Looking for oldforms?8 9 ``django.forms`` was once called ``newforms`` since it replaced Django's10 original form/manipulator/validation framework. The old form handling11 library is still available as `django.oldforms`_, but will be removed12 in a future version of Django.13 14 .. _django.oldforms: ../oldforms/15 16 5 Overview 17 6 ======== 18 7 19 ``django.forms`` is intended to handle HTML form display, data processing20 (validation) and redisplay. It's what you use if you want to perform 21 server-side validation for an HTML form.8 ``django.forms`` is Django's form-handling library. It is intended to handle 9 HTML form display, data processing (validation) and redisplay. It's what you 10 use if you want to perform server-side validation for an HTML form. 22 11 23 12 For example, if your Web site has a contact form that visitors can use to 24 13 send you e-mail, you'd use this library to implement the display of the HTML -
docs/model-api.txt
=== modified file 'docs/model-api.txt'
738 738 739 739 Like ``unique_for_date`` and ``unique_for_month``. 740 740 741 ``validator_list``742 ~~~~~~~~~~~~~~~~~~743 744 A list of extra validators to apply to the field. Each should be a callable745 that takes the parameters ``field_data, all_data`` and raises746 ``django.core.validators.ValidationError`` for errors. (See the747 `validator docs`_.)748 749 Django comes with quite a few validators. They're in ``django.core.validators``.750 751 .. _validator docs: ../oldforms/#validators752 753 741 Verbose field names 754 742 ------------------- 755 743 -
docs/oldforms.txt
=== removed file 'docs/oldforms.txt'
1 ===============================2 Forms, fields, and manipulators3 ===============================4 5 Forwards-compatibility note6 ===========================7 8 The legacy forms/manipulators system described in this document is going to be9 replaced in the next Django release. If you're starting from scratch, we10 strongly encourage you not to waste your time learning this. Instead, learn and11 use the new `forms library`_.12 13 .. _forms library: ../forms/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 accidently create236 # duplicate entires, 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 `generic views`_ documentation if all she362 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. Takes688 a file path to the location of the schema and an optional root element689 (which is wrapped around the XML fragment before validation, if supplied).690 At validation time, the XML fragment is validated against the schema using691 the executable specified in the ``JING_PATH`` setting (see the settings_692 document for more details).693 694 .. _`generic views`: ../generic_views/695 .. _`models API`: ../model-api/696 .. _settings: ../settings/ -
docs/testing.txt
=== modified file 'docs/testing.txt'
857 857 rendered on the form. 858 858 859 859 ``form`` is the name the ``Form`` instance was given in the template 860 context. Note that this works only for ``forms.Form`` instances, not 861 ``oldforms.Form`` instances. 860 context. 862 861 863 862 ``field`` is the name of the field on the form to check. If ``field`` 864 863 has a value of ``None``, non-field errors (errors you can access via -
tests/modeltests/field_subclassing/models.py
=== modified file 'tests/modeltests/field_subclassing/models.py'
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/manipulators/models.py
=== removed directory 'tests/modeltests/manipulators' === removed file 'tests/modeltests/manipulators/__init__.py' === removed file 'tests/modeltests/manipulators/models.py'
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 """}