mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Removed oldforms, validators, and related code:
* Removed `Manipulator`, `AutomaticManipulator`, and related classes.
 * Removed oldforms specific bits from model fields:
   * Removed `validator_list` and `core` arguments from constructors.
   * Removed the methods:
     * `get_manipulator_field_names`
     * `get_manipulator_field_objs`
     * `get_manipulator_fields`
     * `get_manipulator_new_data`
     * `prepare_field_objs_and_params`
     * `get_follow`
   * Renamed `flatten_data` method to `value_to_string` for better alignment with its use by the serialization framework, which was the only remaining code using `flatten_data`.
 * Removed oldforms methods from `django.db.models.Options` class: `get_followed_related_objects`, `get_data_holders`, `get_follow`, and `has_field_type`.
 * Removed oldforms-admin specific options from `django.db.models.fields.related` classes: `num_in_admin`, `min_num_in_admin`, `max_num_in_admin`, `num_extra_on_change`, and `edit_inline`.
 * Serialization framework
   * `Serializer.get_string_value` now calls the model fields' renamed `value_to_string` methods.
   * Removed a special-casing of `models.DateTimeField` in `core.serializers.base.Serializer.get_string_value` that's handled by `django.db.models.fields.DateTimeField.value_to_string`.
 * Removed `django.core.validators`:
   * Moved `ValidationError` exception to `django.core.exceptions`.
   * For the couple places that were using validators, brought over the necessary code to maintain the same functionality.
 * Introduced a SlugField form field for validation and to compliment the SlugField model field (refs #8040).
 * Removed an oldforms-style model creation hack (refs #2160).
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8616 bcc190cf-cafb-0310-a4f2-bffc1f526a37
			
			
This commit is contained in:
		| @@ -85,7 +85,7 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_ | ||||
|                         perms_needed.add(related.opts.verbose_name) | ||||
|                         # We don't care about populating deleted_objects now. | ||||
|                         continue | ||||
|                 if related.field.rel.edit_inline or not has_admin: | ||||
|                 if not has_admin: | ||||
|                     # Don't display link to edit, because it either has no | ||||
|                     # admin or is edited inline. | ||||
|                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []]) | ||||
| @@ -101,7 +101,7 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_ | ||||
|             has_related_objs = False | ||||
|             for sub_obj in getattr(obj, rel_opts_name).all(): | ||||
|                 has_related_objs = True | ||||
|                 if related.field.rel.edit_inline or not has_admin: | ||||
|                 if not has_admin: | ||||
|                     # Don't display link to edit, because it either has no | ||||
|                     # admin or is edited inline. | ||||
|                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []]) | ||||
| @@ -132,7 +132,7 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_ | ||||
|  | ||||
|         if has_related_objs: | ||||
|             for sub_obj in rel_objs.all(): | ||||
|                 if related.field.rel.edit_inline or not has_admin: | ||||
|                 if not has_admin: | ||||
|                     # Don't display link to edit, because it either has no | ||||
|                     # admin or is edited inline. | ||||
|                     nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \ | ||||
|   | ||||
| @@ -8,10 +8,19 @@ import re | ||||
| import sys | ||||
| from optparse import make_option | ||||
| from django.contrib.auth.models import User | ||||
| from django.core import validators | ||||
| from django.core import exceptions | ||||
| from django.core.management.base import BaseCommand, CommandError | ||||
| from django.utils.translation import ugettext as _ | ||||
|  | ||||
| RE_VALID_USERNAME = re.compile('\w+$') | ||||
| EMAIL_RE = re.compile( | ||||
|     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom | ||||
|     r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string | ||||
|     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain | ||||
|  | ||||
| def is_valid_email(value): | ||||
|     if not EMAIL_RE.search(value): | ||||
|         raise exceptions.ValidationError(_('Enter a valid e-mail address.')) | ||||
|  | ||||
| class Command(BaseCommand): | ||||
|     option_list = BaseCommand.option_list + ( | ||||
| @@ -39,8 +48,8 @@ class Command(BaseCommand): | ||||
|             if not RE_VALID_USERNAME.match(username): | ||||
|                 raise CommandError("Invalid username. Use only letters, digits, and underscores") | ||||
|             try: | ||||
|                 validators.isValidEmail(email, None) | ||||
|             except validators.ValidationError: | ||||
|                 is_valid_email(email) | ||||
|             except exceptions.ValidationError: | ||||
|                 raise CommandError("Invalid email address.") | ||||
|  | ||||
|         password = '' | ||||
| @@ -94,8 +103,8 @@ class Command(BaseCommand): | ||||
|                     if not email: | ||||
|                         email = raw_input('E-mail address: ') | ||||
|                     try: | ||||
|                         validators.isValidEmail(email, None) | ||||
|                     except validators.ValidationError: | ||||
|                         is_valid_email(email) | ||||
|                     except exceptions.ValidationError: | ||||
|                         sys.stderr.write("Error: That e-mail address is invalid.\n") | ||||
|                         email = None | ||||
|                     else: | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| from django.contrib import auth | ||||
| from django.core import validators | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.db import models | ||||
| from django.db.models.manager import EmptyManager | ||||
|   | ||||
| @@ -117,9 +117,6 @@ class CommentForm(forms.Form): | ||||
|         """ | ||||
|         comment = self.cleaned_data["comment"] | ||||
|         if settings.COMMENTS_ALLOW_PROFANITIES == False: | ||||
|             # Logic adapted from django.core.validators; it's not clear if they | ||||
|             # should be used in newforms or will be deprecated along with the | ||||
|             # rest of oldforms | ||||
|             bad_words = [w for w in settings.PROFANITIES_LIST if w in comment.lower()] | ||||
|             if bad_words: | ||||
|                 plural = len(bad_words) > 1 | ||||
|   | ||||
| @@ -5,7 +5,7 @@ from django.contrib.contenttypes import generic | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.contrib.sites.models import Site | ||||
| from django.db import models | ||||
| from django.core import urlresolvers, validators | ||||
| from django.core import urlresolvers | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.conf import settings | ||||
|  | ||||
|   | ||||
| @@ -2,18 +2,16 @@ | ||||
| Classes allowing "generic" relations through ContentType and object-id fields. | ||||
| """ | ||||
|  | ||||
| from django import oldforms | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.db import connection | ||||
| from django.db.models import signals | ||||
| from django.db import models | ||||
| from django.db.models.fields.related import RelatedField, Field, ManyToManyRel | ||||
| from django.db.models.loading import get_model | ||||
| from django.utils.functional import curry | ||||
|  | ||||
| from django.forms import ModelForm | ||||
| from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance | ||||
| from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets | ||||
| from django.utils.encoding import smart_unicode | ||||
|  | ||||
| class GenericForeignKey(object): | ||||
|     """ | ||||
| @@ -120,19 +118,12 @@ class GenericRelation(RelatedField, Field): | ||||
|         kwargs['serialize'] = False | ||||
|         Field.__init__(self, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         choices = self.get_choices_default() | ||||
|         return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] | ||||
|  | ||||
|     def get_choices_default(self): | ||||
|         return Field.get_choices(self, include_blank=False) | ||||
|  | ||||
|     def flatten_data(self, follow, obj = None): | ||||
|         new_data = {} | ||||
|         if obj: | ||||
|             instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()] | ||||
|             new_data[self.name] = instance_ids | ||||
|         return new_data | ||||
|     def value_to_string(self, obj): | ||||
|         qs = getattr(obj, self.name).all() | ||||
|         return smart_unicode([instance._get_pk_val() for instance in qs]) | ||||
|  | ||||
|     def m2m_db_table(self): | ||||
|         return self.rel.to._meta.db_table | ||||
| @@ -290,7 +281,6 @@ class GenericRel(ManyToManyRel): | ||||
|         self.to = to | ||||
|         self.related_name = related_name | ||||
|         self.limit_choices_to = limit_choices_to or {} | ||||
|         self.edit_inline = False | ||||
|         self.symmetrical = symmetrical | ||||
|         self.multiple = True | ||||
|  | ||||
| @@ -300,7 +290,7 @@ class BaseGenericInlineFormSet(BaseModelFormSet): | ||||
|     """ | ||||
|     ct_field_name = "content_type" | ||||
|     ct_fk_field_name = "object_id" | ||||
|      | ||||
|  | ||||
|     def __init__(self, data=None, files=None, instance=None, save_as_new=None): | ||||
|         opts = self.model._meta | ||||
|         self.instance = instance | ||||
| @@ -395,4 +385,3 @@ class GenericStackedInline(GenericInlineModelAdmin): | ||||
|  | ||||
| class GenericTabularInline(GenericInlineModelAdmin): | ||||
|     template = 'admin/edit_inline/tabular.html' | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| from django.core import validators | ||||
| from django.db import models | ||||
| from django.contrib.sites.models import Site | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| JP-specific Form helpers | ||||
| """ | ||||
|  | ||||
| from django.core import validators | ||||
| from django.forms import ValidationError | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.forms.fields import RegexField, Select | ||||
|   | ||||
| @@ -32,3 +32,6 @@ class FieldError(Exception): | ||||
|     """Some kind of problem with a model field.""" | ||||
|     pass | ||||
|  | ||||
| class ValidationError(Exception): | ||||
|     """An error while validating data.""" | ||||
|     pass | ||||
|   | ||||
| @@ -57,12 +57,7 @@ class Serializer(object): | ||||
|         """ | ||||
|         Convert a field's value to a string. | ||||
|         """ | ||||
|         if isinstance(field, models.DateTimeField): | ||||
|             d = datetime_safe.new_datetime(getattr(obj, field.name)) | ||||
|             value = d.strftime("%Y-%m-%d %H:%M:%S") | ||||
|         else: | ||||
|             value = field.flatten_data(follow=None, obj=obj).get(field.name, "") | ||||
|         return smart_unicode(value) | ||||
|         return smart_unicode(field.value_to_string(obj)) | ||||
|  | ||||
|     def start_serialization(self): | ||||
|         """ | ||||
|   | ||||
| @@ -1,598 +0,0 @@ | ||||
| """ | ||||
| A library of validators that return None and raise ValidationError when the | ||||
| provided data isn't valid. | ||||
|  | ||||
| Validators may be callable classes, and they may have an 'always_test' | ||||
| attribute. If an 'always_test' attribute exists (regardless of value), the | ||||
| validator will *always* be run, regardless of whether its associated | ||||
| form field is required. | ||||
| """ | ||||
|  | ||||
| import urllib2 | ||||
| import re | ||||
| try: | ||||
|     from decimal import Decimal, DecimalException | ||||
| except ImportError: | ||||
|     from django.utils._decimal import Decimal, DecimalException    # Python 2.3 | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.utils.translation import ugettext as _, ugettext_lazy, ungettext | ||||
| from django.utils.functional import Promise, lazy | ||||
| from django.utils.encoding import force_unicode, smart_str | ||||
|  | ||||
| _datere = r'\d{4}-\d{1,2}-\d{1,2}' | ||||
| _timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' | ||||
| alnum_re = re.compile(r'^\w+$') | ||||
| alnumurl_re = re.compile(r'^[-\w/]+$') | ||||
| ansi_date_re = re.compile('^%s$' % _datere) | ||||
| ansi_time_re = re.compile('^%s$' % _timere) | ||||
| ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere)) | ||||
| email_re = re.compile( | ||||
|     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom | ||||
|     r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string | ||||
|     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain | ||||
| integer_re = re.compile(r'^-?\d+$') | ||||
| ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') | ||||
| phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) | ||||
| slug_re = re.compile(r'^[-\w]+$') | ||||
| url_re = re.compile(r'^https?://\S+$') | ||||
|  | ||||
| lazy_inter = lazy(lambda a,b: force_unicode(a) % b, unicode) | ||||
|  | ||||
| class ValidationError(Exception): | ||||
|     def __init__(self, message): | ||||
|         "ValidationError can be passed a string or a list." | ||||
|         if isinstance(message, list): | ||||
|             self.messages = [force_unicode(msg) for msg in message] | ||||
|         else: | ||||
|             assert isinstance(message, (basestring, Promise)), ("%s should be a string" % repr(message)) | ||||
|             self.messages = [force_unicode(message)] | ||||
|  | ||||
|     def __str__(self): | ||||
|         # This is needed because, without a __str__(), printing an exception | ||||
|         # instance would result in this: | ||||
|         # AttributeError: ValidationError instance has no attribute 'args' | ||||
|         # See http://www.python.org/doc/current/tut/node10.html#handling | ||||
|         return str(self.messages) | ||||
|  | ||||
| class CriticalValidationError(Exception): | ||||
|     def __init__(self, message): | ||||
|         "ValidationError can be passed a string or a list." | ||||
|         if isinstance(message, list): | ||||
|             self.messages = [force_unicode(msg) for msg in message] | ||||
|         else: | ||||
|             assert isinstance(message, (basestring, Promise)), ("'%s' should be a string" % message) | ||||
|             self.messages = [force_unicode(message)] | ||||
|  | ||||
|     def __str__(self): | ||||
|         return str(self.messages) | ||||
|  | ||||
| def isAlphaNumeric(field_data, all_data): | ||||
|     if not alnum_re.search(field_data): | ||||
|         raise ValidationError, _("This value must contain only letters, numbers and underscores.") | ||||
|  | ||||
| def isAlphaNumericURL(field_data, all_data): | ||||
|     if not alnumurl_re.search(field_data): | ||||
|         raise ValidationError, _("This value must contain only letters, numbers, underscores, dashes or slashes.") | ||||
|  | ||||
| def isSlug(field_data, all_data): | ||||
|     if not slug_re.search(field_data): | ||||
|         raise ValidationError, _("This value must contain only letters, numbers, underscores or hyphens.") | ||||
|  | ||||
| def isLowerCase(field_data, all_data): | ||||
|     if field_data.lower() != field_data: | ||||
|         raise ValidationError, _("Uppercase letters are not allowed here.") | ||||
|  | ||||
| def isUpperCase(field_data, all_data): | ||||
|     if field_data.upper() != field_data: | ||||
|         raise ValidationError, _("Lowercase letters are not allowed here.") | ||||
|  | ||||
| def isCommaSeparatedIntegerList(field_data, all_data): | ||||
|     for supposed_int in field_data.split(','): | ||||
|         try: | ||||
|             int(supposed_int) | ||||
|         except ValueError: | ||||
|             raise ValidationError, _("Enter only digits separated by commas.") | ||||
|  | ||||
| def isCommaSeparatedEmailList(field_data, all_data): | ||||
|     """ | ||||
|     Checks that field_data is a string of e-mail addresses separated by commas. | ||||
|     Blank field_data values will not throw a validation error, and whitespace | ||||
|     is allowed around the commas. | ||||
|     """ | ||||
|     for supposed_email in field_data.split(','): | ||||
|         try: | ||||
|             isValidEmail(supposed_email.strip(), '') | ||||
|         except ValidationError: | ||||
|             raise ValidationError, _("Enter valid e-mail addresses separated by commas.") | ||||
|  | ||||
| def isValidIPAddress4(field_data, all_data): | ||||
|     if not ip4_re.search(field_data): | ||||
|         raise ValidationError, _("Please enter a valid IP address.") | ||||
|  | ||||
| def isNotEmpty(field_data, all_data): | ||||
|     if field_data.strip() == '': | ||||
|         raise ValidationError, _("Empty values are not allowed here.") | ||||
|  | ||||
| def isOnlyDigits(field_data, all_data): | ||||
|     if not field_data.isdigit(): | ||||
|         raise ValidationError, _("Non-numeric characters aren't allowed here.") | ||||
|  | ||||
| def isNotOnlyDigits(field_data, all_data): | ||||
|     if field_data.isdigit(): | ||||
|         raise ValidationError, _("This value can't be comprised solely of digits.") | ||||
|  | ||||
| def isInteger(field_data, all_data): | ||||
|     # This differs from isOnlyDigits because this accepts the negative sign | ||||
|     if not integer_re.search(field_data): | ||||
|         raise ValidationError, _("Enter a whole number.") | ||||
|  | ||||
| def isOnlyLetters(field_data, all_data): | ||||
|     if not field_data.isalpha(): | ||||
|         raise ValidationError, _("Only alphabetical characters are allowed here.") | ||||
|  | ||||
| def _isValidDate(date_string): | ||||
|     """ | ||||
|     A helper function used by isValidANSIDate and isValidANSIDatetime to | ||||
|     check if the date is valid.  The date string is assumed to already be in | ||||
|     YYYY-MM-DD format. | ||||
|     """ | ||||
|     from datetime import date | ||||
|     # Could use time.strptime here and catch errors, but datetime.date below | ||||
|     # produces much friendlier error messages. | ||||
|     year, month, day = map(int, date_string.split('-')) | ||||
|     try: | ||||
|         date(year, month, day) | ||||
|     except ValueError, e: | ||||
|         msg = _('Invalid date: %s') % _(str(e)) | ||||
|         raise ValidationError, msg | ||||
|  | ||||
| def isValidANSIDate(field_data, all_data): | ||||
|     if not ansi_date_re.search(field_data): | ||||
|         raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.') | ||||
|     _isValidDate(field_data) | ||||
|  | ||||
| def isValidANSITime(field_data, all_data): | ||||
|     if not ansi_time_re.search(field_data): | ||||
|         raise ValidationError, _('Enter a valid time in HH:MM format.') | ||||
|  | ||||
| def isValidANSIDatetime(field_data, all_data): | ||||
|     if not ansi_datetime_re.search(field_data): | ||||
|         raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') | ||||
|     _isValidDate(field_data.split()[0]) | ||||
|  | ||||
| def isValidEmail(field_data, all_data): | ||||
|     if not email_re.search(field_data): | ||||
|         raise ValidationError, _('Enter a valid e-mail address.') | ||||
|  | ||||
| def isValidImage(field_data, all_data): | ||||
|     """ | ||||
|     Checks that the file-upload field data contains a valid image (GIF, JPG, | ||||
|     PNG, possibly others -- whatever the Python Imaging Library supports). | ||||
|     """ | ||||
|     from PIL import Image | ||||
|     from cStringIO import StringIO | ||||
|     try: | ||||
|         content = field_data.read() | ||||
|     except TypeError: | ||||
|         raise ValidationError, _("No file was submitted. Check the encoding type on the form.") | ||||
|     try: | ||||
|         # load() is the only method that can spot a truncated JPEG, | ||||
|         #  but it cannot be called sanely after verify() | ||||
|         trial_image = Image.open(StringIO(content)) | ||||
|         trial_image.load() | ||||
|         # verify() is the only method that can spot a corrupt PNG, | ||||
|         #  but it must be called immediately after the constructor | ||||
|         trial_image = Image.open(StringIO(content)) | ||||
|         trial_image.verify() | ||||
|     except Exception: # Python Imaging Library doesn't recognize it as an image | ||||
|         raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.") | ||||
|  | ||||
| def isValidImageURL(field_data, all_data): | ||||
|     uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png')) | ||||
|     try: | ||||
|         uc(field_data, all_data) | ||||
|     except URLMimeTypeCheck.InvalidContentType: | ||||
|         raise ValidationError, _("The URL %s does not point to a valid image.") % field_data | ||||
|  | ||||
| def isValidPhone(field_data, all_data): | ||||
|     if not phone_re.search(field_data): | ||||
|         raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data | ||||
|  | ||||
| def isValidQuicktimeVideoURL(field_data, all_data): | ||||
|     "Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)" | ||||
|     uc = URLMimeTypeCheck(('video/quicktime', 'video/mpeg',)) | ||||
|     try: | ||||
|         uc(field_data, all_data) | ||||
|     except URLMimeTypeCheck.InvalidContentType: | ||||
|         raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data | ||||
|  | ||||
| def isValidURL(field_data, all_data): | ||||
|     if not url_re.search(field_data): | ||||
|         raise ValidationError, _("A valid URL is required.") | ||||
|  | ||||
| def isValidHTML(field_data, all_data): | ||||
|     import urllib, urllib2 | ||||
|     try: | ||||
|         u = urllib2.urlopen('http://validator.w3.org/check', urllib.urlencode({'fragment': field_data, 'output': 'xml'})) | ||||
|     except: | ||||
|         # Validator or Internet connection is unavailable. Fail silently. | ||||
|         return | ||||
|     html_is_valid = (u.headers.get('x-w3c-validator-status', 'Invalid') == 'Valid') | ||||
|     if html_is_valid: | ||||
|         return | ||||
|     from xml.dom.minidom import parseString | ||||
|     error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')] | ||||
|     raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages) | ||||
|  | ||||
| def isWellFormedXml(field_data, all_data): | ||||
|     from xml.dom.minidom import parseString | ||||
|     try: | ||||
|         parseString(field_data) | ||||
|     except Exception, e: # Naked except because we're not sure what will be thrown | ||||
|         raise ValidationError, _("Badly formed XML: %s") % str(e) | ||||
|  | ||||
| def isWellFormedXmlFragment(field_data, all_data): | ||||
|     isWellFormedXml('<root>%s</root>' % field_data, all_data) | ||||
|  | ||||
| def isExistingURL(field_data, all_data): | ||||
|     try: | ||||
|         headers = { | ||||
|             "Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", | ||||
|             "Accept-Language" : "en-us,en;q=0.5", | ||||
|             "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", | ||||
|             "Connection" : "close", | ||||
|             "User-Agent": settings.URL_VALIDATOR_USER_AGENT | ||||
|             } | ||||
|         req = urllib2.Request(field_data,None, headers) | ||||
|         u = urllib2.urlopen(req) | ||||
|     except ValueError: | ||||
|         raise ValidationError, _("Invalid URL: %s") % field_data | ||||
|     except urllib2.HTTPError, e: | ||||
|         # 401s are valid; they just mean authorization is required. | ||||
|         # 301 and 302 are redirects; they just mean look somewhere else. | ||||
|         if str(e.code) not in ('401','301','302'): | ||||
|             raise ValidationError, _("The URL %s is a broken link.") % field_data | ||||
|     except: # urllib2.URLError, httplib.InvalidURL, etc. | ||||
|         raise ValidationError, _("The URL %s is a broken link.") % field_data | ||||
|  | ||||
| def isValidUSState(field_data, all_data): | ||||
|     "Checks that the given string is a valid two-letter U.S. state abbreviation" | ||||
|     states = ['AA', 'AE', 'AK', 'AL', 'AP', 'AR', 'AS', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'FM', 'GA', 'GU', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MH', 'MI', 'MN', 'MO', 'MP', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'PR', 'PW', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VI', 'VT', 'WA', 'WI', 'WV', 'WY'] | ||||
|     if field_data.upper() not in states: | ||||
|         raise ValidationError, _("Enter a valid U.S. state abbreviation.") | ||||
|  | ||||
| def hasNoProfanities(field_data, all_data): | ||||
|     """ | ||||
|     Checks that the given string has no profanities in it. This does a simple | ||||
|     check for whether each profanity exists within the string, so 'fuck' will | ||||
|     catch 'motherfucker' as well. Raises a ValidationError such as: | ||||
|         Watch your mouth! The words "f--k" and "s--t" are not allowed here. | ||||
|     """ | ||||
|     field_data = field_data.lower() # normalize | ||||
|     words_seen = [w for w in settings.PROFANITIES_LIST if w in field_data] | ||||
|     if words_seen: | ||||
|         from django.utils.text import get_text_list | ||||
|         plural = len(words_seen) | ||||
|         raise ValidationError, ungettext("Watch your mouth! The word %s is not allowed here.", | ||||
|             "Watch your mouth! The words %s are not allowed here.", plural) % \ | ||||
|             get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], _('and')) | ||||
|  | ||||
| class AlwaysMatchesOtherField(object): | ||||
|     def __init__(self, other_field_name, error_message=None): | ||||
|         self.other = other_field_name | ||||
|         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must match the '%s' field."), self.other) | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if field_data != all_data[self.other]: | ||||
|             raise ValidationError, self.error_message | ||||
|  | ||||
| class ValidateIfOtherFieldEquals(object): | ||||
|     def __init__(self, other_field, other_value, validator_list): | ||||
|         self.other_field, self.other_value = other_field, other_value | ||||
|         self.validator_list = validator_list | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if self.other_field in all_data and all_data[self.other_field] == self.other_value: | ||||
|             for v in self.validator_list: | ||||
|                 v(field_data, all_data) | ||||
|  | ||||
| class RequiredIfOtherFieldNotGiven(object): | ||||
|     def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter something for at least one field.")): | ||||
|         self.other, self.error_message = other_field_name, error_message | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if not all_data.get(self.other, False) and not field_data: | ||||
|             raise ValidationError, self.error_message | ||||
|  | ||||
| class RequiredIfOtherFieldsGiven(object): | ||||
|     def __init__(self, other_field_names, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): | ||||
|         self.other, self.error_message = other_field_names, error_message | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         for field in self.other: | ||||
|             if all_data.get(field, False) and not field_data: | ||||
|                 raise ValidationError, self.error_message | ||||
|  | ||||
| class RequiredIfOtherFieldGiven(RequiredIfOtherFieldsGiven): | ||||
|     "Like RequiredIfOtherFieldsGiven, but takes a single field name instead of a list." | ||||
|     def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): | ||||
|         RequiredIfOtherFieldsGiven.__init__(self, [other_field_name], error_message) | ||||
|  | ||||
| class RequiredIfOtherFieldEquals(object): | ||||
|     def __init__(self, other_field, other_value, error_message=None, other_label=None): | ||||
|         self.other_field = other_field | ||||
|         self.other_value = other_value | ||||
|         other_label = other_label or other_value | ||||
|         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is %(value)s"), { | ||||
|             'field': other_field, 'value': other_label}) | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if self.other_field in all_data and all_data[self.other_field] == self.other_value and not field_data: | ||||
|             raise ValidationError(self.error_message) | ||||
|  | ||||
| class RequiredIfOtherFieldDoesNotEqual(object): | ||||
|     def __init__(self, other_field, other_value, other_label=None, error_message=None): | ||||
|         self.other_field = other_field | ||||
|         self.other_value = other_value | ||||
|         other_label = other_label or other_value | ||||
|         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is not %(value)s"), { | ||||
|             'field': other_field, 'value': other_label}) | ||||
|         self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if self.other_field in all_data and all_data[self.other_field] != self.other_value and not field_data: | ||||
|             raise ValidationError(self.error_message) | ||||
|  | ||||
| class IsLessThanOtherField(object): | ||||
|     def __init__(self, other_field_name, error_message): | ||||
|         self.other, self.error_message = other_field_name, error_message | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if field_data > all_data[self.other]: | ||||
|             raise ValidationError, self.error_message | ||||
|  | ||||
| class UniqueAmongstFieldsWithPrefix(object): | ||||
|     def __init__(self, field_name, prefix, error_message): | ||||
|         self.field_name, self.prefix = field_name, prefix | ||||
|         self.error_message = error_message or ugettext_lazy("Duplicate values are not allowed.") | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         for field_name, value in all_data.items(): | ||||
|             if field_name != self.field_name and value == field_data: | ||||
|                 raise ValidationError, self.error_message | ||||
|  | ||||
| class NumberIsInRange(object): | ||||
|     """ | ||||
|     Validator that tests if a value is in a range (inclusive). | ||||
|     """ | ||||
|     def __init__(self, lower=None, upper=None, error_message=''): | ||||
|         self.lower, self.upper = lower, upper | ||||
|         if not error_message: | ||||
|             if lower and upper: | ||||
|                  self.error_message = _("This value must be between %(lower)s and %(upper)s.") % {'lower': lower, 'upper': upper} | ||||
|             elif lower: | ||||
|                 self.error_message = _("This value must be at least %s.") % lower | ||||
|             elif upper: | ||||
|                 self.error_message = _("This value must be no more than %s.") % upper | ||||
|         else: | ||||
|             self.error_message = error_message | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         # Try to make the value numeric. If this fails, we assume another | ||||
|         # validator will catch the problem. | ||||
|         try: | ||||
|             val = float(field_data) | ||||
|         except ValueError: | ||||
|             return | ||||
|  | ||||
|         # Now validate | ||||
|         if self.lower and self.upper and (val < self.lower or val > self.upper): | ||||
|             raise ValidationError(self.error_message) | ||||
|         elif self.lower and val < self.lower: | ||||
|             raise ValidationError(self.error_message) | ||||
|         elif self.upper and val > self.upper: | ||||
|             raise ValidationError(self.error_message) | ||||
|  | ||||
| class IsAPowerOf(object): | ||||
|     """ | ||||
|     Usage: If you create an instance of the IsPowerOf validator: | ||||
|         v = IsAPowerOf(2) | ||||
|  | ||||
|     The following calls will succeed: | ||||
|         v(4, None) | ||||
|         v(8, None) | ||||
|         v(16, None) | ||||
|  | ||||
|     But this call: | ||||
|         v(17, None) | ||||
|     will raise "django.core.validators.ValidationError: ['This value must be a power of 2.']" | ||||
|     """ | ||||
|     def __init__(self, power_of): | ||||
|         self.power_of = power_of | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         from math import log | ||||
|         val = log(int(field_data)) / log(self.power_of) | ||||
|         if val != int(val): | ||||
|             raise ValidationError, _("This value must be a power of %s.") % self.power_of | ||||
|  | ||||
| class IsValidDecimal(object): | ||||
|     def __init__(self, max_digits, decimal_places): | ||||
|         self.max_digits, self.decimal_places = max_digits, decimal_places | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         try: | ||||
|             val = Decimal(field_data) | ||||
|         except DecimalException: | ||||
|             raise ValidationError, _("Please enter a valid decimal number.") | ||||
|  | ||||
|         pieces = str(val).lstrip("-").split('.') | ||||
|         decimals = (len(pieces) == 2) and len(pieces[1]) or 0 | ||||
|         digits = len(pieces[0]) | ||||
|  | ||||
|         if digits + decimals > self.max_digits: | ||||
|             raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.", | ||||
|                 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits | ||||
|         if digits > (self.max_digits - self.decimal_places): | ||||
|             raise ValidationError, ungettext( "Please enter a valid decimal number with a whole part of at most %s digit.", | ||||
|                 "Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places) | ||||
|         if decimals > self.decimal_places: | ||||
|             raise ValidationError, ungettext("Please enter a valid decimal number with at most %s decimal place.", | ||||
|                 "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places | ||||
|  | ||||
| def isValidFloat(field_data, all_data): | ||||
|     data = smart_str(field_data) | ||||
|     try: | ||||
|         float(data) | ||||
|     except ValueError: | ||||
|         raise ValidationError, _("Please enter a valid floating point number.") | ||||
|  | ||||
| class HasAllowableSize(object): | ||||
|     """ | ||||
|     Checks that the file-upload field data is a certain size. min_size and | ||||
|     max_size are measurements in bytes. | ||||
|     """ | ||||
|     def __init__(self, min_size=None, max_size=None, min_error_message=None, max_error_message=None): | ||||
|         self.min_size, self.max_size = min_size, max_size | ||||
|         self.min_error_message = min_error_message or lazy_inter(ugettext_lazy("Make sure your uploaded file is at least %s bytes big."), min_size) | ||||
|         self.max_error_message = max_error_message or lazy_inter(ugettext_lazy("Make sure your uploaded file is at most %s bytes big."), max_size) | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         try: | ||||
|             content = field_data.read() | ||||
|         except TypeError: | ||||
|             raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.") | ||||
|         if self.min_size is not None and len(content) < self.min_size: | ||||
|             raise ValidationError, self.min_error_message | ||||
|         if self.max_size is not None and len(content) > self.max_size: | ||||
|             raise ValidationError, self.max_error_message | ||||
|  | ||||
| class MatchesRegularExpression(object): | ||||
|     """ | ||||
|     Checks that the field matches the given regular-expression. The regex | ||||
|     should be in string format, not already compiled. | ||||
|     """ | ||||
|     def __init__(self, regexp, error_message=ugettext_lazy("The format for this field is wrong.")): | ||||
|         self.regexp = re.compile(regexp) | ||||
|         self.error_message = error_message | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         if not self.regexp.search(field_data): | ||||
|             raise ValidationError(self.error_message) | ||||
|  | ||||
| class AnyValidator(object): | ||||
|     """ | ||||
|     This validator tries all given validators. If any one of them succeeds, | ||||
|     validation passes. If none of them succeeds, the given message is thrown | ||||
|     as a validation error. The message is rather unspecific, so it's best to | ||||
|     specify one on instantiation. | ||||
|     """ | ||||
|     def __init__(self, validator_list=None, error_message=ugettext_lazy("This field is invalid.")): | ||||
|         if validator_list is None: validator_list = [] | ||||
|         self.validator_list = validator_list | ||||
|         self.error_message = error_message | ||||
|         for v in validator_list: | ||||
|             if hasattr(v, 'always_test'): | ||||
|                 self.always_test = True | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         for v in self.validator_list: | ||||
|             try: | ||||
|                 v(field_data, all_data) | ||||
|                 return | ||||
|             except ValidationError, e: | ||||
|                 pass | ||||
|         raise ValidationError(self.error_message) | ||||
|  | ||||
| class URLMimeTypeCheck(object): | ||||
|     "Checks that the provided URL points to a document with a listed mime type" | ||||
|     class CouldNotRetrieve(ValidationError): | ||||
|         pass | ||||
|     class InvalidContentType(ValidationError): | ||||
|         pass | ||||
|  | ||||
|     def __init__(self, mime_type_list): | ||||
|         self.mime_type_list = mime_type_list | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         import urllib2 | ||||
|         try: | ||||
|             isValidURL(field_data, all_data) | ||||
|         except ValidationError: | ||||
|             raise | ||||
|         try: | ||||
|             info = urllib2.urlopen(field_data).info() | ||||
|         except (urllib2.HTTPError, urllib2.URLError): | ||||
|             raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data | ||||
|         content_type = info['content-type'] | ||||
|         if content_type not in self.mime_type_list: | ||||
|             raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % { | ||||
|                 'url': field_data, 'contenttype': content_type} | ||||
|  | ||||
| class RelaxNGCompact(object): | ||||
|     "Validate against a Relax NG compact schema" | ||||
|     def __init__(self, schema_path, additional_root_element=None): | ||||
|         self.schema_path = schema_path | ||||
|         self.additional_root_element = additional_root_element | ||||
|  | ||||
|     def __call__(self, field_data, all_data): | ||||
|         import os, tempfile | ||||
|         if self.additional_root_element: | ||||
|             field_data = '<%(are)s>%(data)s\n</%(are)s>' % { | ||||
|                 'are': self.additional_root_element, | ||||
|                 'data': field_data | ||||
|             } | ||||
|         filename = tempfile.mktemp() # Insecure, but nothing else worked | ||||
|         fp = open(filename, 'w') | ||||
|         fp.write(field_data) | ||||
|         fp.close() | ||||
|         if not os.path.exists(settings.JING_PATH): | ||||
|             raise Exception, "%s not found!" % settings.JING_PATH | ||||
|         p = os.popen('%s -c %s %s' % (settings.JING_PATH, self.schema_path, filename)) | ||||
|         errors = [line.strip() for line in p.readlines()] | ||||
|         p.close() | ||||
|         os.unlink(filename) | ||||
|         display_errors = [] | ||||
|         lines = field_data.split('\n') | ||||
|         for error in errors: | ||||
|             ignored, line, level, message = error.split(':', 3) | ||||
|             # Scrape the Jing error messages to reword them more nicely. | ||||
|             m = re.search(r'Expected "(.*?)" to terminate element starting on line (\d+)', message) | ||||
|             if m: | ||||
|                 display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \ | ||||
|                     {'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]}) | ||||
|                 continue | ||||
|             if message.strip() == 'text not allowed here': | ||||
|                 display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \ | ||||
|                     {'line':line, 'start':lines[int(line) - 1][:30]}) | ||||
|                 continue | ||||
|             m = re.search(r'\s*attribute "(.*?)" not allowed at this point; ignored', message) | ||||
|             if m: | ||||
|                 display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \ | ||||
|                     {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) | ||||
|                 continue | ||||
|             m = re.search(r'\s*unknown element "(.*?)"', message) | ||||
|             if m: | ||||
|                 display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \ | ||||
|                     {'tag':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) | ||||
|                 continue | ||||
|             if message.strip() == 'required attributes missing': | ||||
|                 display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \ | ||||
|                     {'line':line, 'start':lines[int(line) - 1][:30]}) | ||||
|                 continue | ||||
|             m = re.search(r'\s*bad value for attribute "(.*?)"', message) | ||||
|             if m: | ||||
|                 display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \ | ||||
|                     {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) | ||||
|                 continue | ||||
|             # Failing all those checks, use the default error message. | ||||
|             display_error = 'Line %s: %s [%s]' % (line, message, level.strip()) | ||||
|             display_errors.append(display_error) | ||||
|         if len(display_errors) > 0: | ||||
|             raise ValidationError, display_errors | ||||
| @@ -1,6 +1,5 @@ | ||||
| from django.conf import settings | ||||
| from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured | ||||
| from django.core import validators | ||||
| from django.db import connection | ||||
| from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models | ||||
| from django.db.models.query import Q | ||||
| @@ -9,7 +8,7 @@ from django.db.models.base import Model | ||||
| from django.db.models.fields import * | ||||
| from django.db.models.fields.subclassing import SubfieldBase | ||||
| from django.db.models.fields.files import FileField, ImageField | ||||
| from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED | ||||
| from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel | ||||
| from django.db.models import signals | ||||
|  | ||||
| # Admin stages. | ||||
|   | ||||
| @@ -8,9 +8,7 @@ try: | ||||
| except NameError: | ||||
|     from sets import Set as set     # Python 2.3 fallback. | ||||
|  | ||||
| import django.db.models.manipulators    # Imported to register signal handler. | ||||
| import django.db.models.manager         # Ditto. | ||||
| from django.core import validators | ||||
| import django.db.models.manager     # Imported to register signal handler. | ||||
| from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError | ||||
| from django.db.models.fields import AutoField | ||||
| from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField | ||||
| @@ -320,9 +318,7 @@ class Model(object): | ||||
|  | ||||
|         # First, try an UPDATE. If that doesn't update anything, do an INSERT. | ||||
|         pk_val = self._get_pk_val(meta) | ||||
|         # Note: the comparison with '' is required for compatibility with | ||||
|         # oldforms-style model creation. | ||||
|         pk_set = pk_val is not None and smart_unicode(pk_val) != u'' | ||||
|         pk_set = pk_val is not None | ||||
|         record_exists = True | ||||
|         manager = cls._default_manager | ||||
|         if pk_set: | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import copy | ||||
| import datetime | ||||
| import os | ||||
| import re | ||||
| import time | ||||
| try: | ||||
|     import decimal | ||||
| @@ -12,10 +13,8 @@ from django.db.models import signals | ||||
| from django.db.models.query_utils import QueryWrapper | ||||
| from django.dispatch import dispatcher | ||||
| from django.conf import settings | ||||
| from django.core import validators | ||||
| from django import oldforms | ||||
| from django import forms | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.core import exceptions | ||||
| from django.utils.datastructures import DictWrapper | ||||
| from django.utils.functional import curry | ||||
| from django.utils.itercompat import tee | ||||
| @@ -34,17 +33,6 @@ BLANK_CHOICE_NONE = [("", "None")] | ||||
| class FieldDoesNotExist(Exception): | ||||
|     pass | ||||
|  | ||||
| def manipulator_validator_unique(f, opts, self, field_data, all_data): | ||||
|     "Validates that the value is unique for this field." | ||||
|     lookup_type = f.get_validator_unique_lookup_type() | ||||
|     try: | ||||
|         old_obj = self.manager.get(**{lookup_type: field_data}) | ||||
|     except ObjectDoesNotExist: | ||||
|         return | ||||
|     if getattr(self, 'original_object', None) and self.original_object._get_pk_val() == old_obj._get_pk_val(): | ||||
|         return | ||||
|     raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name} | ||||
|  | ||||
| # A guide to Field parameters: | ||||
| # | ||||
| #   * name:      The name of the field specifed in the model. | ||||
| @@ -73,11 +61,10 @@ class Field(object): | ||||
|  | ||||
|     def __init__(self, verbose_name=None, name=None, primary_key=False, | ||||
|             max_length=None, unique=False, blank=False, null=False, | ||||
|             db_index=False, core=False, rel=None, default=NOT_PROVIDED, | ||||
|             editable=True, serialize=True, unique_for_date=None, | ||||
|             unique_for_month=None, unique_for_year=None, validator_list=None, | ||||
|             choices=None, help_text='', db_column=None, db_tablespace=None, | ||||
|             auto_created=False): | ||||
|             db_index=False, rel=None, default=NOT_PROVIDED, editable=True, | ||||
|             serialize=True, unique_for_date=None, unique_for_month=None, | ||||
|             unique_for_year=None, choices=None, help_text='', db_column=None, | ||||
|             db_tablespace=None, auto_created=False): | ||||
|         self.name = name | ||||
|         self.verbose_name = verbose_name | ||||
|         self.primary_key = primary_key | ||||
| @@ -87,10 +74,10 @@ class Field(object): | ||||
|         # option whenever '' is a possible value. | ||||
|         if self.empty_strings_allowed and connection.features.interprets_empty_strings_as_nulls: | ||||
|             self.null = True | ||||
|         self.core, self.rel, self.default = core, rel, default | ||||
|         self.rel = rel | ||||
|         self.default = default | ||||
|         self.editable = editable | ||||
|         self.serialize = serialize | ||||
|         self.validator_list = validator_list or [] | ||||
|         self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month | ||||
|         self.unique_for_year = unique_for_year | ||||
|         self._choices = choices or [] | ||||
| @@ -126,8 +113,8 @@ class Field(object): | ||||
|     def to_python(self, value): | ||||
|         """ | ||||
|         Converts the input value into the expected Python data type, raising | ||||
|         validators.ValidationError if the data can't be converted. Returns the | ||||
|         converted value. Subclasses should override this. | ||||
|         django.core.exceptions.ValidationError if the data can't be converted. | ||||
|         Returns the converted value. Subclasses should override this. | ||||
|         """ | ||||
|         return value | ||||
|  | ||||
| @@ -252,93 +239,9 @@ class Field(object): | ||||
|             return None | ||||
|         return "" | ||||
|  | ||||
|     def get_manipulator_field_names(self, name_prefix): | ||||
|         """ | ||||
|         Returns a list of field names that this object adds to the manipulator. | ||||
|         """ | ||||
|         return [name_prefix + self.name] | ||||
|  | ||||
|     def prepare_field_objs_and_params(self, manipulator, name_prefix): | ||||
|         params = {'validator_list': self.validator_list[:]} | ||||
|         if self.max_length and not self.choices: # Don't give SelectFields a max_length parameter. | ||||
|             params['max_length'] = self.max_length | ||||
|  | ||||
|         if self.choices: | ||||
|             field_objs = [oldforms.SelectField] | ||||
|  | ||||
|             params['choices'] = self.get_flatchoices() | ||||
|         else: | ||||
|             field_objs = self.get_manipulator_field_objs() | ||||
|         return (field_objs, params) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): | ||||
|         """ | ||||
|         Returns a list of oldforms.FormField instances for this field. It | ||||
|         calculates the choices at runtime, not at compile time. | ||||
|  | ||||
|         name_prefix is a prefix to prepend to the "field_name" argument. | ||||
|         rel is a boolean specifying whether this field is in a related context. | ||||
|         """ | ||||
|         field_objs, params = self.prepare_field_objs_and_params(manipulator, name_prefix) | ||||
|  | ||||
|         # Add the "unique" validator(s). | ||||
|         for field_name_list in opts.unique_together: | ||||
|             if field_name_list[0] == self.name: | ||||
|                 params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list))) | ||||
|  | ||||
|         # Add the "unique for..." validator(s). | ||||
|         if self.unique_for_date: | ||||
|             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date))) | ||||
|         if self.unique_for_month: | ||||
|             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month))) | ||||
|         if self.unique_for_year: | ||||
|             params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year))) | ||||
|         if self.unique and not rel: | ||||
|             params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator)) | ||||
|  | ||||
|         # Only add is_required=True if the field cannot be blank. Primary keys | ||||
|         # are a special case, and fields in a related context should set this | ||||
|         # as False, because they'll be caught by a separate validator -- | ||||
|         # RequiredIfOtherFieldGiven. | ||||
|         params['is_required'] = not self.blank and not self.primary_key and not rel | ||||
|  | ||||
|         # BooleanFields (CheckboxFields) are a special case. They don't take | ||||
|         # is_required. | ||||
|         if isinstance(self, BooleanField): | ||||
|             del params['is_required'] | ||||
|  | ||||
|         # If this field is in a related context, check whether any other fields | ||||
|         # in the related object have core=True. If so, add a validator -- | ||||
|         # RequiredIfOtherFieldsGiven -- to this FormField. | ||||
|         if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField): | ||||
|             # First, get the core fields, if any. | ||||
|             core_field_names = [] | ||||
|             for f in opts.fields: | ||||
|                 if f.core and f != self: | ||||
|                     core_field_names.extend(f.get_manipulator_field_names(name_prefix)) | ||||
|             # Now, if there are any, add the validator to this FormField. | ||||
|             if core_field_names: | ||||
|                 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, ugettext_lazy("This field is required."))) | ||||
|  | ||||
|         # Finally, add the field_names. | ||||
|         field_names = self.get_manipulator_field_names(name_prefix) | ||||
|         return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)] | ||||
|  | ||||
|     def get_validator_unique_lookup_type(self): | ||||
|         return '%s__exact' % self.name | ||||
|  | ||||
|     def get_manipulator_new_data(self, new_data, rel=False): | ||||
|         """ | ||||
|         Given the full new_data dictionary (from the manipulator), returns this | ||||
|         field's data. | ||||
|         """ | ||||
|         if rel: | ||||
|             return new_data.get(self.name, [self.get_default()])[0] | ||||
|         val = new_data.get(self.name, self.get_default()) | ||||
|         if not self.empty_strings_allowed and val == '' and self.null: | ||||
|             val = None | ||||
|         return val | ||||
|  | ||||
|     def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): | ||||
|         """Returns choices with a default blank choices included, for use | ||||
|         as SelectField choices for this field.""" | ||||
| @@ -366,19 +269,12 @@ class Field(object): | ||||
|         else: | ||||
|             return self.get_default() | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|     def value_to_string(self, obj): | ||||
|         """ | ||||
|         Returns a dictionary mapping the field's manipulator field names to its | ||||
|         "flattened" string values for the admin view. obj is the instance to | ||||
|         extract the values from. | ||||
|         Returns a string value of this field from the passed obj. | ||||
|         This is used by the serialization framework. | ||||
|         """ | ||||
|         return {self.attname: self._get_val_from_obj(obj)} | ||||
|  | ||||
|     def get_follow(self, override=None): | ||||
|         if override != None: | ||||
|             return override | ||||
|         else: | ||||
|             return self.editable | ||||
|         return smart_unicode(self._get_val_from_obj(obj)) | ||||
|  | ||||
|     def bind(self, fieldmapping, original, bound_field_class): | ||||
|         return bound_field_class(self, fieldmapping, original) | ||||
| @@ -432,29 +328,14 @@ class AutoField(Field): | ||||
|         try: | ||||
|             return int(value) | ||||
|         except (TypeError, ValueError): | ||||
|             raise validators.ValidationError, _("This value must be an integer.") | ||||
|             raise exceptions.ValidationError( | ||||
|                 _("This value must be an integer.")) | ||||
|  | ||||
|     def get_db_prep_value(self, value): | ||||
|         if value is None: | ||||
|             return None | ||||
|         return int(value) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): | ||||
|         if not rel: | ||||
|             return [] # Don't add a FormField unless it's in a related context. | ||||
|         return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.HiddenField] | ||||
|  | ||||
|     def get_manipulator_new_data(self, new_data, rel=False): | ||||
|         # Never going to be called | ||||
|         # Not in main change pages | ||||
|         # ignored in related context | ||||
|         if not rel: | ||||
|             return None | ||||
|         return Field.get_manipulator_new_data(self, new_data, rel) | ||||
|  | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         assert not cls._meta.has_auto_field, "A model can't have more than one AutoField." | ||||
|         super(AutoField, self).contribute_to_class(cls, name) | ||||
| @@ -478,25 +359,20 @@ class BooleanField(Field): | ||||
|         if value in (True, False): return value | ||||
|         if value in ('t', 'True', '1'): return True | ||||
|         if value in ('f', 'False', '0'): return False | ||||
|         raise validators.ValidationError, _("This value must be either True or False.") | ||||
|         raise exceptions.ValidationError( | ||||
|             _("This value must be either True or False.")) | ||||
|  | ||||
|     def get_db_prep_value(self, value): | ||||
|         if value is None: | ||||
|             return None | ||||
|         return bool(value) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.CheckboxField] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.BooleanField} | ||||
|         defaults.update(kwargs) | ||||
|         return super(BooleanField, self).formfield(**defaults) | ||||
|  | ||||
| class CharField(Field): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.TextField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "CharField" | ||||
|  | ||||
| @@ -507,7 +383,8 @@ class CharField(Field): | ||||
|             if self.null: | ||||
|                 return value | ||||
|             else: | ||||
|                 raise validators.ValidationError, ugettext_lazy("This field cannot be null.") | ||||
|                 raise exceptions.ValidationError( | ||||
|                     ugettext_lazy("This field cannot be null.")) | ||||
|         return smart_unicode(value) | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
| @@ -517,8 +394,9 @@ class CharField(Field): | ||||
|  | ||||
| # TODO: Maybe move this into contrib, because it's specialized. | ||||
| class CommaSeparatedIntegerField(CharField): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.CommaSeparatedIntegerField] | ||||
|     pass | ||||
|  | ||||
| ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$') | ||||
|  | ||||
| class DateField(Field): | ||||
|     empty_strings_allowed = False | ||||
| @@ -540,11 +418,20 @@ class DateField(Field): | ||||
|             return value.date() | ||||
|         if isinstance(value, datetime.date): | ||||
|             return value | ||||
|         validators.isValidANSIDate(value, None) | ||||
|  | ||||
|         if not ansi_date_re.search(value): | ||||
|             raise exceptions.ValidationError( | ||||
|                 _('Enter a valid date in YYYY-MM-DD format.')) | ||||
|         # Now that we have the date string in YYYY-MM-DD format, check to make | ||||
|         # sure it's a valid date. | ||||
|         # We could use time.strptime here and catch errors, but datetime.date | ||||
|         # produces much friendlier error messages. | ||||
|         year, month, day = map(int, value.split('-')) | ||||
|         try: | ||||
|             return datetime.date(*time.strptime(value, '%Y-%m-%d')[:3]) | ||||
|         except ValueError: | ||||
|             raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') | ||||
|             return datetime.date(year, month, day) | ||||
|         except ValueError, e: | ||||
|             msg = _('Invalid date: %s') % _(str(e)) | ||||
|             raise exceptions.ValidationError(msg) | ||||
|  | ||||
|     def pre_save(self, model_instance, add): | ||||
|         if self.auto_now or (self.auto_now_add and add): | ||||
| @@ -562,13 +449,6 @@ class DateField(Field): | ||||
|             setattr(cls, 'get_previous_by_%s' % self.name, | ||||
|                 curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=False)) | ||||
|  | ||||
|     # Needed because of horrible auto_now[_add] behaviour wrt. editable | ||||
|     def get_follow(self, override=None): | ||||
|         if override != None: | ||||
|             return override | ||||
|         else: | ||||
|             return self.editable or self.auto_now or self.auto_now_add | ||||
|  | ||||
|     def get_db_prep_lookup(self, lookup_type, value): | ||||
|         # For "__month" and "__day" lookups, convert the value to a string so | ||||
|         # the database backend always sees a consistent type. | ||||
| @@ -580,16 +460,13 @@ class DateField(Field): | ||||
|         # Casts dates into the format expected by the backend | ||||
|         return connection.ops.value_to_db_date(self.to_python(value)) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.DateField] | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|     def value_to_string(self, obj): | ||||
|         val = self._get_val_from_obj(obj) | ||||
|         if val is None: | ||||
|             data = '' | ||||
|         else: | ||||
|             data = datetime_safe.new_date(val).strftime("%Y-%m-%d") | ||||
|         return {self.attname: data} | ||||
|         return data | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.DateField} | ||||
| @@ -616,7 +493,8 @@ class DateTimeField(DateField): | ||||
|                 value, usecs = value.split('.') | ||||
|                 usecs = int(usecs) | ||||
|             except ValueError: | ||||
|                 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.') | ||||
|                 raise exceptions.ValidationError( | ||||
|                     _('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.')) | ||||
|         else: | ||||
|             usecs = 0 | ||||
|         kwargs = {'microsecond': usecs} | ||||
| @@ -633,40 +511,21 @@ class DateTimeField(DateField): | ||||
|                     return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], | ||||
|                                              **kwargs) | ||||
|                 except ValueError: | ||||
|                     raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.') | ||||
|                     raise exceptions.ValidationError( | ||||
|                         _('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.')) | ||||
|  | ||||
|     def get_db_prep_value(self, value): | ||||
|         # Casts dates into the format expected by the backend | ||||
|         return connection.ops.value_to_db_datetime(self.to_python(value)) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.DateField, oldforms.TimeField] | ||||
|  | ||||
|     def get_manipulator_field_names(self, name_prefix): | ||||
|         return [name_prefix + self.name + '_date', name_prefix + self.name + '_time'] | ||||
|  | ||||
|     def get_manipulator_new_data(self, new_data, rel=False): | ||||
|         date_field, time_field = self.get_manipulator_field_names('') | ||||
|         if rel: | ||||
|             d = new_data.get(date_field, [None])[0] | ||||
|             t = new_data.get(time_field, [None])[0] | ||||
|         else: | ||||
|             d = new_data.get(date_field, None) | ||||
|             t = new_data.get(time_field, None) | ||||
|         if d is not None and t is not None: | ||||
|             return datetime.datetime.combine(d, t) | ||||
|         return self.get_default() | ||||
|  | ||||
|     def flatten_data(self,follow, obj = None): | ||||
|     def value_to_string(self, obj): | ||||
|         val = self._get_val_from_obj(obj) | ||||
|         date_field, time_field = self.get_manipulator_field_names('') | ||||
|         if val is None: | ||||
|             date_data = time_data = '' | ||||
|             data = '' | ||||
|         else: | ||||
|             d = datetime_safe.new_datetime(val) | ||||
|             date_data = d.strftime('%Y-%m-%d') | ||||
|             time_data = d.strftime('%H:%M:%S') | ||||
|         return {date_field: date_data, time_field: time_data} | ||||
|             data = d.strftime('%Y-%m-%d %H:%M:%S') | ||||
|         return data | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.DateTimeField} | ||||
| @@ -688,7 +547,7 @@ class DecimalField(Field): | ||||
|         try: | ||||
|             return decimal.Decimal(value) | ||||
|         except decimal.InvalidOperation: | ||||
|             raise validators.ValidationError( | ||||
|             raise exceptions.ValidationError( | ||||
|                 _("This value must be a decimal number.")) | ||||
|  | ||||
|     def _format(self, value): | ||||
| @@ -715,9 +574,6 @@ class DecimalField(Field): | ||||
|         return connection.ops.value_to_db_decimal(self.to_python(value), | ||||
|                 self.max_digits, self.decimal_places) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = { | ||||
|             'max_digits': self.max_digits, | ||||
| @@ -732,9 +588,6 @@ class EmailField(CharField): | ||||
|         kwargs['max_length'] = kwargs.get('max_length', 75) | ||||
|         CharField.__init__(self, *args, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.EmailField] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.EmailField} | ||||
|         defaults.update(kwargs) | ||||
| @@ -756,9 +609,6 @@ class FilePathField(Field): | ||||
|         defaults.update(kwargs) | ||||
|         return super(FilePathField, self).formfield(**defaults) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "FilePathField" | ||||
|  | ||||
| @@ -770,9 +620,6 @@ class FloatField(Field): | ||||
|             return None | ||||
|         return float(value) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.FloatField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "FloatField" | ||||
|  | ||||
| @@ -788,9 +635,6 @@ class IntegerField(Field): | ||||
|             return None | ||||
|         return int(value) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.IntegerField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "IntegerField" | ||||
|  | ||||
| @@ -800,8 +644,9 @@ class IntegerField(Field): | ||||
|         try: | ||||
|             return int(value) | ||||
|         except (TypeError, ValueError): | ||||
|             raise validators.ValidationError, _("This value must be an integer.") | ||||
|                  | ||||
|             raise exceptions.ValidationError( | ||||
|                 _("This value must be an integer.")) | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.IntegerField} | ||||
|         defaults.update(kwargs) | ||||
| @@ -813,9 +658,6 @@ class IPAddressField(Field): | ||||
|         kwargs['max_length'] = 15 | ||||
|         Field.__init__(self, *args, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.IPAddressField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "IPAddressField" | ||||
|  | ||||
| @@ -838,16 +680,14 @@ class NullBooleanField(Field): | ||||
|         if value in ('None'): return None | ||||
|         if value in ('t', 'True', '1'): return True | ||||
|         if value in ('f', 'False', '0'): return False | ||||
|         raise validators.ValidationError, _("This value must be either None, True or False.") | ||||
|         raise exceptions.ValidationError( | ||||
|             _("This value must be either None, True or False.")) | ||||
|  | ||||
|     def get_db_prep_value(self, value): | ||||
|         if value is None: | ||||
|             return None | ||||
|         return bool(value) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.NullBooleanField] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = { | ||||
|             'form_class': forms.NullBooleanField, | ||||
| @@ -858,9 +698,6 @@ class NullBooleanField(Field): | ||||
|         return super(NullBooleanField, self).formfield(**defaults) | ||||
|  | ||||
| class PhoneNumberField(Field): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.PhoneNumberField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "PhoneNumberField" | ||||
|  | ||||
| @@ -871,9 +708,6 @@ class PhoneNumberField(Field): | ||||
|         return super(PhoneNumberField, self).formfield(**defaults) | ||||
|  | ||||
| class PositiveIntegerField(IntegerField): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.PositiveIntegerField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "PositiveIntegerField" | ||||
|  | ||||
| @@ -883,9 +717,6 @@ class PositiveIntegerField(IntegerField): | ||||
|         return super(PositiveIntegerField, self).formfield(**defaults) | ||||
|  | ||||
| class PositiveSmallIntegerField(IntegerField): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.PositiveSmallIntegerField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "PositiveSmallIntegerField" | ||||
|  | ||||
| @@ -897,7 +728,6 @@ class PositiveSmallIntegerField(IntegerField): | ||||
| class SlugField(CharField): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         kwargs['max_length'] = kwargs.get('max_length', 50) | ||||
|         kwargs.setdefault('validator_list', []).append(validators.isSlug) | ||||
|         # Set db_index=True unless it's been set manually. | ||||
|         if 'db_index' not in kwargs: | ||||
|             kwargs['db_index'] = True | ||||
| @@ -907,23 +737,15 @@ class SlugField(CharField): | ||||
|         return "SlugField" | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.RegexField, 'regex': r'^[a-zA-Z0-9_-]+$', | ||||
|             'error_messages': {'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.")}, | ||||
|         } | ||||
|         defaults = {'form_class': forms.SlugField} | ||||
|         defaults.update(kwargs) | ||||
|         return super(SlugField, self).formfield(**defaults) | ||||
|  | ||||
| class SmallIntegerField(IntegerField): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.SmallIntegerField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "SmallIntegerField" | ||||
|  | ||||
| class TextField(Field): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.LargeTextField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "TextField" | ||||
|  | ||||
| @@ -957,7 +779,8 @@ class TimeField(Field): | ||||
|                 value, usecs = value.split('.') | ||||
|                 usecs = int(usecs) | ||||
|             except ValueError: | ||||
|                 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') | ||||
|                 raise exceptions.ValidationError( | ||||
|                     _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.')) | ||||
|         else: | ||||
|             usecs = 0 | ||||
|         kwargs = {'microsecond': usecs} | ||||
| @@ -970,7 +793,8 @@ class TimeField(Field): | ||||
|                 return datetime.time(*time.strptime(value, '%H:%M')[3:5], | ||||
|                                          **kwargs) | ||||
|             except ValueError: | ||||
|                 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') | ||||
|                 raise exceptions.ValidationError( | ||||
|                     _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.')) | ||||
|  | ||||
|     def pre_save(self, model_instance, add): | ||||
|         if self.auto_now or (self.auto_now_add and add): | ||||
| @@ -984,12 +808,13 @@ class TimeField(Field): | ||||
|         # Casts times into the format expected by the backend | ||||
|         return connection.ops.value_to_db_time(self.to_python(value)) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.TimeField] | ||||
|  | ||||
|     def flatten_data(self,follow, obj = None): | ||||
|     def value_to_string(self, obj): | ||||
|         val = self._get_val_from_obj(obj) | ||||
|         return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} | ||||
|         if val is None: | ||||
|             data = '' | ||||
|         else: | ||||
|             data = val.strftime("%H:%M:%S") | ||||
|         return data | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.TimeField} | ||||
| @@ -999,23 +824,15 @@ class TimeField(Field): | ||||
| class URLField(CharField): | ||||
|     def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): | ||||
|         kwargs['max_length'] = kwargs.get('max_length', 200) | ||||
|         if verify_exists: | ||||
|             kwargs.setdefault('validator_list', []).append(validators.isExistingURL) | ||||
|         self.verify_exists = verify_exists | ||||
|         CharField.__init__(self, verbose_name, name, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.URLField] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.URLField, 'verify_exists': self.verify_exists} | ||||
|         defaults.update(kwargs) | ||||
|         return super(URLField, self).formfield(**defaults) | ||||
|  | ||||
| class USStateField(Field): | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.USStateField] | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "USStateField" | ||||
|  | ||||
| @@ -1029,7 +846,3 @@ class XMLField(TextField): | ||||
|     def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs): | ||||
|         self.schema_path = schema_path | ||||
|         Field.__init__(self, verbose_name, name, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)] | ||||
|  | ||||
|   | ||||
| @@ -11,9 +11,7 @@ from django.utils.functional import curry | ||||
| from django.db.models import signals | ||||
| from django.utils.encoding import force_unicode, smart_str | ||||
| from django.utils.translation import ugettext_lazy, ugettext as _ | ||||
| from django import oldforms | ||||
| from django import forms | ||||
| from django.core import validators | ||||
| from django.db.models.loading import cache | ||||
|  | ||||
| class FieldFile(File): | ||||
| @@ -126,7 +124,7 @@ class FileField(Field): | ||||
|     attr_class = FieldFile | ||||
|  | ||||
|     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): | ||||
|         for arg in ('core', 'primary_key', 'unique'): | ||||
|         for arg in ('primary_key', 'unique'): | ||||
|             if arg in kwargs: | ||||
|                 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__)) | ||||
|  | ||||
| @@ -153,42 +151,6 @@ class FileField(Field): | ||||
|             return None | ||||
|         return unicode(value) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): | ||||
|         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) | ||||
|         if not self.blank: | ||||
|             if rel: | ||||
|                 # This validator makes sure FileFields work in a related context. | ||||
|                 class RequiredFileField(object): | ||||
|                     def __init__(self, other_field_names, other_file_field_name): | ||||
|                         self.other_field_names = other_field_names | ||||
|                         self.other_file_field_name = other_file_field_name | ||||
|                         self.always_test = True | ||||
|                     def __call__(self, field_data, all_data): | ||||
|                         if not all_data.get(self.other_file_field_name, False): | ||||
|                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required.")) | ||||
|                             c(field_data, all_data) | ||||
|                 # First, get the core fields, if any. | ||||
|                 core_field_names = [] | ||||
|                 for f in opts.fields: | ||||
|                     if f.core and f != self: | ||||
|                         core_field_names.extend(f.get_manipulator_field_names(name_prefix)) | ||||
|                 # Now, if there are any, add the validator to this FormField. | ||||
|                 if core_field_names: | ||||
|                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name)) | ||||
|             else: | ||||
|                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required.")) | ||||
|                 v.always_test = True | ||||
|                 field_list[0].validator_list.append(v) | ||||
|                 field_list[0].is_required = field_list[1].is_required = False | ||||
|  | ||||
|         # If the raw path is passed in, validate it's under the MEDIA_ROOT. | ||||
|         def isWithinMediaRoot(field_data, all_data): | ||||
|             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data)) | ||||
|             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))): | ||||
|                 raise validators.ValidationError(_("Enter a valid filename.")) | ||||
|         field_list[1].validator_list.append(isWithinMediaRoot) | ||||
|         return field_list | ||||
|  | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         super(FileField, self).contribute_to_class(cls, name) | ||||
|         setattr(cls, self.name, FileDescriptor(self)) | ||||
| @@ -206,14 +168,9 @@ class FileField(Field): | ||||
|             # Otherwise, just close the file, so it doesn't tie up resources. | ||||
|             file.close() | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.FileUploadField, oldforms.HiddenField] | ||||
|  | ||||
|     def get_manipulator_field_names(self, name_prefix): | ||||
|         return [name_prefix + self.name + '_file', name_prefix + self.name] | ||||
|  | ||||
|     def save_file(self, new_data, new_object, original_object, change, rel, save=True): | ||||
|         upload_field_name = self.get_manipulator_field_names('')[0] | ||||
|     def save_file(self, new_data, new_object, original_object, change, rel, | ||||
|                   save=True): | ||||
|         upload_field_name = self.name + '_file' | ||||
|         if new_data.get(upload_field_name, False): | ||||
|             if rel: | ||||
|                 file = new_data[upload_field_name][0] | ||||
| @@ -282,9 +239,6 @@ class ImageField(FileField): | ||||
|         self.width_field, self.height_field = width_field, height_field | ||||
|         FileField.__init__(self, verbose_name, name, **kwargs) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         return [oldforms.ImageUploadField, oldforms.HiddenField] | ||||
|  | ||||
|     def formfield(self, **kwargs): | ||||
|         defaults = {'form_class': forms.ImageField} | ||||
|         defaults.update(kwargs) | ||||
|   | ||||
| @@ -4,10 +4,10 @@ from django.db.models.fields import AutoField, Field, IntegerField, PositiveInte | ||||
| from django.db.models.related import RelatedObject | ||||
| from django.db.models.query import QuerySet | ||||
| from django.db.models.query_utils import QueryWrapper | ||||
| from django.utils.encoding import smart_unicode | ||||
| from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ | ||||
| from django.utils.functional import curry | ||||
| from django.core import validators | ||||
| from django import oldforms | ||||
| from django.core import exceptions | ||||
| from django import forms | ||||
|  | ||||
| try: | ||||
| @@ -15,9 +15,6 @@ try: | ||||
| except NameError: | ||||
|     from sets import Set as set   # Python 2.3 fallback | ||||
|  | ||||
| # Values for Relation.edit_inline. | ||||
| TABULAR, STACKED = 1, 2 | ||||
|  | ||||
| RECURSIVE_RELATIONSHIP_CONSTANT = 'self' | ||||
|  | ||||
| pending_lookups = {} | ||||
| @@ -83,14 +80,6 @@ def do_pending_lookups(sender, **kwargs): | ||||
|  | ||||
| signals.class_prepared.connect(do_pending_lookups) | ||||
|  | ||||
| def manipulator_valid_rel_key(f, self, field_data, all_data): | ||||
|     "Validates that the value is a valid foreign key" | ||||
|     klass = f.rel.to | ||||
|     try: | ||||
|         klass._default_manager.get(**{f.rel.field_name: field_data}) | ||||
|     except klass.DoesNotExist: | ||||
|         raise validators.ValidationError, _("Please enter a valid %s.") % f.verbose_name | ||||
|  | ||||
| #HACK | ||||
| class RelatedField(object): | ||||
|     def contribute_to_class(self, cls, name): | ||||
| @@ -580,18 +569,14 @@ class ReverseManyRelatedObjectsDescriptor(object): | ||||
|         manager.add(*value) | ||||
|  | ||||
| class ManyToOneRel(object): | ||||
|     def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, | ||||
|             max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, | ||||
|             related_name=None, limit_choices_to=None, lookup_overrides=None, | ||||
|             parent_link=False): | ||||
|     def __init__(self, to, field_name, related_name=None, | ||||
|             limit_choices_to=None, lookup_overrides=None, parent_link=False): | ||||
|         try: | ||||
|             to._meta | ||||
|         except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT | ||||
|             assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT | ||||
|         self.to, self.field_name = to, field_name | ||||
|         self.num_in_admin, self.edit_inline = num_in_admin, edit_inline | ||||
|         self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin | ||||
|         self.num_extra_on_change, self.related_name = num_extra_on_change, related_name | ||||
|         self.related_name = related_name | ||||
|         if limit_choices_to is None: | ||||
|             limit_choices_to = {} | ||||
|         self.limit_choices_to = limit_choices_to | ||||
| @@ -611,29 +596,21 @@ class ManyToOneRel(object): | ||||
|         return data[0] | ||||
|  | ||||
| class OneToOneRel(ManyToOneRel): | ||||
|     def __init__(self, to, field_name, num_in_admin=0, min_num_in_admin=None, | ||||
|             max_num_in_admin=None, num_extra_on_change=None, edit_inline=False, | ||||
|             related_name=None, limit_choices_to=None, lookup_overrides=None, | ||||
|             parent_link=False): | ||||
|         # NOTE: *_num_in_admin and num_extra_on_change are intentionally | ||||
|         # ignored here. We accept them as parameters only to match the calling | ||||
|         # signature of ManyToOneRel.__init__(). | ||||
|         super(OneToOneRel, self).__init__(to, field_name, num_in_admin, | ||||
|                 edit_inline=edit_inline, related_name=related_name, | ||||
|                 limit_choices_to=limit_choices_to, | ||||
|     def __init__(self, to, field_name, related_name=None, | ||||
|             limit_choices_to=None, lookup_overrides=None, parent_link=False): | ||||
|         super(OneToOneRel, self).__init__(to, field_name, | ||||
|                 related_name=related_name, limit_choices_to=limit_choices_to, | ||||
|                 lookup_overrides=lookup_overrides, parent_link=parent_link) | ||||
|         self.multiple = False | ||||
|  | ||||
| class ManyToManyRel(object): | ||||
|     def __init__(self, to, num_in_admin=0, related_name=None, | ||||
|         limit_choices_to=None, symmetrical=True, through=None): | ||||
|     def __init__(self, to, related_name=None, limit_choices_to=None, | ||||
|             symmetrical=True, through=None): | ||||
|         self.to = to | ||||
|         self.num_in_admin = num_in_admin | ||||
|         self.related_name = related_name | ||||
|         if limit_choices_to is None: | ||||
|             limit_choices_to = {} | ||||
|         self.limit_choices_to = limit_choices_to | ||||
|         self.edit_inline = False | ||||
|         self.symmetrical = symmetrical | ||||
|         self.multiple = True | ||||
|         self.through = through | ||||
| @@ -651,11 +628,6 @@ class ForeignKey(RelatedField, Field): | ||||
|         kwargs['verbose_name'] = kwargs.get('verbose_name', None) | ||||
|  | ||||
|         kwargs['rel'] = rel_class(to, to_field, | ||||
|             num_in_admin=kwargs.pop('num_in_admin', 3), | ||||
|             min_num_in_admin=kwargs.pop('min_num_in_admin', None), | ||||
|             max_num_in_admin=kwargs.pop('max_num_in_admin', None), | ||||
|             num_extra_on_change=kwargs.pop('num_extra_on_change', 1), | ||||
|             edit_inline=kwargs.pop('edit_inline', False), | ||||
|             related_name=kwargs.pop('related_name', None), | ||||
|             limit_choices_to=kwargs.pop('limit_choices_to', None), | ||||
|             lookup_overrides=kwargs.pop('lookup_overrides', None), | ||||
| @@ -670,15 +642,6 @@ class ForeignKey(RelatedField, Field): | ||||
|     def get_validator_unique_lookup_type(self): | ||||
|         return '%s__%s__exact' % (self.name, self.rel.get_related_field().name) | ||||
|  | ||||
|     def prepare_field_objs_and_params(self, manipulator, name_prefix): | ||||
|         params = {'validator_list': self.validator_list[:], 'member_name': name_prefix + self.attname} | ||||
|         if self.null: | ||||
|             field_objs = [oldforms.NullSelectField] | ||||
|         else: | ||||
|             field_objs = [oldforms.SelectField] | ||||
|         params['choices'] = self.get_choices_default() | ||||
|         return field_objs, params | ||||
|  | ||||
|     def get_default(self): | ||||
|         "Here we check if the default value is an object and return the to_field if so." | ||||
|         field_default = super(ForeignKey, self).get_default() | ||||
| @@ -686,17 +649,13 @@ class ForeignKey(RelatedField, Field): | ||||
|             return getattr(field_default, self.rel.get_related_field().attname) | ||||
|         return field_default | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         rel_field = self.rel.get_related_field() | ||||
|         return [oldforms.IntegerField] | ||||
|  | ||||
|     def get_db_prep_save(self, value): | ||||
|         if value == '' or value == None: | ||||
|             return None | ||||
|         else: | ||||
|             return self.rel.get_related_field().get_db_prep_save(value) | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|     def value_to_string(self, obj): | ||||
|         if not obj: | ||||
|             # In required many-to-one fields with only one available choice, | ||||
|             # select that one available choice. Note: For SelectFields | ||||
| @@ -705,8 +664,8 @@ class ForeignKey(RelatedField, Field): | ||||
|             if not self.blank and self.choices: | ||||
|                 choice_list = self.get_choices_default() | ||||
|                 if len(choice_list) == 2: | ||||
|                     return {self.attname: choice_list[1][0]} | ||||
|         return Field.flatten_data(self, follow, obj) | ||||
|                     return smart_unicode(choice_list[1][0]) | ||||
|         return Field.value_to_string(self, obj) | ||||
|  | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         super(ForeignKey, self).contribute_to_class(cls, name) | ||||
| @@ -744,8 +703,6 @@ class OneToOneField(ForeignKey): | ||||
|     """ | ||||
|     def __init__(self, to, to_field=None, **kwargs): | ||||
|         kwargs['unique'] = True | ||||
|         if 'num_in_admin' not in kwargs: | ||||
|             kwargs['num_in_admin'] = 0 | ||||
|         super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs) | ||||
|  | ||||
|     def contribute_to_related_class(self, cls, related): | ||||
| @@ -768,7 +725,6 @@ class ManyToManyField(RelatedField, Field): | ||||
|  | ||||
|         kwargs['verbose_name'] = kwargs.get('verbose_name', None) | ||||
|         kwargs['rel'] = ManyToManyRel(to, | ||||
|             num_in_admin=kwargs.pop('num_in_admin', 0), | ||||
|             related_name=kwargs.pop('related_name', None), | ||||
|             limit_choices_to=kwargs.pop('limit_choices_to', None), | ||||
|             symmetrical=kwargs.pop('symmetrical', True), | ||||
| @@ -786,10 +742,6 @@ class ManyToManyField(RelatedField, Field): | ||||
|         msg = ugettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') | ||||
|         self.help_text = string_concat(self.help_text, ' ', msg) | ||||
|  | ||||
|     def get_manipulator_field_objs(self): | ||||
|         choices = self.get_choices_default() | ||||
|         return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] | ||||
|  | ||||
|     def get_choices_default(self): | ||||
|         return Field.get_choices(self, include_blank=False) | ||||
|  | ||||
| @@ -863,25 +815,27 @@ class ManyToManyField(RelatedField, Field): | ||||
|         objects = mod._default_manager.in_bulk(pks) | ||||
|         if len(objects) != len(pks): | ||||
|             badkeys = [k for k in pks if k not in objects] | ||||
|             raise validators.ValidationError, ungettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.", | ||||
|                     "Please enter valid %(self)s IDs. The values %(value)r are invalid.", len(badkeys)) % { | ||||
|             raise exceptions.ValidationError( | ||||
|                 ungettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.", | ||||
|                           "Please enter valid %(self)s IDs. The values %(value)r are invalid.", | ||||
|                           len(badkeys)) % { | ||||
|                 'self': self.verbose_name, | ||||
|                 'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys), | ||||
|             } | ||||
|             }) | ||||
|  | ||||
|     def flatten_data(self, follow, obj = None): | ||||
|         new_data = {} | ||||
|     def value_to_string(self, obj): | ||||
|         data = '' | ||||
|         if obj: | ||||
|             instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()] | ||||
|             new_data[self.name] = instance_ids | ||||
|             qs = getattr(obj, self.name).all() | ||||
|             data = [instance._get_pk_val() for instance in qs] | ||||
|         else: | ||||
|             # In required many-to-many fields with only one available choice, | ||||
|             # select that one available choice. | ||||
|             if not self.blank and not self.rel.edit_inline: | ||||
|             if not self.blank: | ||||
|                 choices_list = self.get_choices_default() | ||||
|                 if len(choices_list) == 1: | ||||
|                     new_data[self.name] = [choices_list[0][0]] | ||||
|         return new_data | ||||
|                     data = [choices_list[0][0]] | ||||
|         return smart_unicode(data) | ||||
|  | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         super(ManyToManyField, self).contribute_to_class(cls, name) | ||||
|   | ||||
| @@ -1,333 +0,0 @@ | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django import oldforms | ||||
| from django.core import validators | ||||
| from django.db.models.fields import AutoField | ||||
| from django.db.models.fields.files import FileField | ||||
| from django.db.models import signals | ||||
| from django.utils.functional import curry | ||||
| from django.utils.datastructures import DotExpandedDict | ||||
| from django.utils.text import capfirst | ||||
| from django.utils.encoding import smart_str | ||||
| from django.utils.translation import ugettext as _ | ||||
| from django.utils import datetime_safe | ||||
|  | ||||
| def add_manipulators(sender, **kwargs): | ||||
|     cls = sender | ||||
|     cls.add_to_class('AddManipulator', AutomaticAddManipulator) | ||||
|     cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator) | ||||
|  | ||||
| signals.class_prepared.connect(add_manipulators) | ||||
|  | ||||
| class ManipulatorDescriptor(object): | ||||
|     # This class provides the functionality that makes the default model | ||||
|     # manipulators (AddManipulator and ChangeManipulator) available via the | ||||
|     # model class. | ||||
|     def __init__(self, name, base): | ||||
|         self.man = None # Cache of the manipulator class. | ||||
|         self.name = name | ||||
|         self.base = base | ||||
|  | ||||
|     def __get__(self, instance, model=None): | ||||
|         if instance != None: | ||||
|             raise AttributeError, "Manipulator cannot be accessed via instance" | ||||
|         else: | ||||
|             if not self.man: | ||||
|                 # Create a class that inherits from the "Manipulator" class | ||||
|                 # given in the model class (if specified) and the automatic | ||||
|                 # manipulator. | ||||
|                 bases = [self.base] | ||||
|                 if hasattr(model, 'Manipulator'): | ||||
|                     bases = [model.Manipulator] + bases | ||||
|                 self.man = type(self.name, tuple(bases), {}) | ||||
|                 self.man._prepare(model) | ||||
|             return self.man | ||||
|  | ||||
| class AutomaticManipulator(oldforms.Manipulator): | ||||
|     def _prepare(cls, model): | ||||
|         cls.model = model | ||||
|         cls.manager = model._default_manager | ||||
|         cls.opts = model._meta | ||||
|         for field_name_list in cls.opts.unique_together: | ||||
|             setattr(cls, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, cls.opts)) | ||||
|         for f in cls.opts.fields: | ||||
|             if f.unique_for_date: | ||||
|                 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')) | ||||
|             if f.unique_for_month: | ||||
|                 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')) | ||||
|             if f.unique_for_year: | ||||
|                 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')) | ||||
|     _prepare = classmethod(_prepare) | ||||
|  | ||||
|     def contribute_to_class(cls, other_cls, name): | ||||
|         setattr(other_cls, name, ManipulatorDescriptor(name, cls)) | ||||
|     contribute_to_class = classmethod(contribute_to_class) | ||||
|  | ||||
|     def __init__(self, follow=None): | ||||
|         self.follow = self.opts.get_follow(follow) | ||||
|         self.fields = [] | ||||
|  | ||||
|         for f in self.opts.fields + self.opts.many_to_many: | ||||
|             if self.follow.get(f.name, False): | ||||
|                 self.fields.extend(f.get_manipulator_fields(self.opts, self, self.change)) | ||||
|  | ||||
|         # Add fields for related objects. | ||||
|         for f in self.opts.get_all_related_objects(): | ||||
|             if self.follow.get(f.name, False): | ||||
|                 fol = self.follow[f.name] | ||||
|                 self.fields.extend(f.get_manipulator_fields(self.opts, self, self.change, fol)) | ||||
|  | ||||
|         # Add field for ordering. | ||||
|         if self.change and self.opts.get_ordered_objects(): | ||||
|             self.fields.append(oldforms.CommaSeparatedIntegerField(field_name="order_")) | ||||
|  | ||||
|     def save(self, new_data): | ||||
|         # TODO: big cleanup when core fields go -> use recursive manipulators. | ||||
|         params = {} | ||||
|         for f in self.opts.fields: | ||||
|             # Fields with auto_now_add should keep their original value in the change stage. | ||||
|             auto_now_add = self.change and getattr(f, 'auto_now_add', False) | ||||
|             if self.follow.get(f.name, None) and not auto_now_add: | ||||
|                 param = f.get_manipulator_new_data(new_data) | ||||
|             else: | ||||
|                 if self.change: | ||||
|                     param = getattr(self.original_object, f.attname) | ||||
|                 else: | ||||
|                     param = f.get_default() | ||||
|             params[f.attname] = param | ||||
|  | ||||
|         if self.change: | ||||
|             params[self.opts.pk.attname] = self.obj_key | ||||
|  | ||||
|         # First, create the basic object itself. | ||||
|         new_object = self.model(**params) | ||||
|  | ||||
|         # Now that the object's been created, save any uploaded files. | ||||
|         for f in self.opts.fields: | ||||
|             if isinstance(f, FileField): | ||||
|                 f.save_file(new_data, new_object, self.change and self.original_object or None, self.change, rel=False, save=False) | ||||
|  | ||||
|         # Now save the object | ||||
|         new_object.save() | ||||
|  | ||||
|         # Calculate which primary fields have changed. | ||||
|         if self.change: | ||||
|             self.fields_added, self.fields_changed, self.fields_deleted = [], [], [] | ||||
|             for f in self.opts.fields: | ||||
|                 if not f.primary_key and smart_str(getattr(self.original_object, f.attname)) != smart_str(getattr(new_object, f.attname)): | ||||
|                     self.fields_changed.append(f.verbose_name) | ||||
|  | ||||
|         # Save many-to-many objects. Example: Set sites for a poll. | ||||
|         for f in self.opts.many_to_many: | ||||
|             if self.follow.get(f.name, None): | ||||
|                 if not f.rel.edit_inline: | ||||
|                     new_vals = new_data.getlist(f.name) | ||||
|                     # First, clear the existing values. | ||||
|                     rel_manager = getattr(new_object, f.name) | ||||
|                     rel_manager.clear() | ||||
|                     # Then, set the new values. | ||||
|                     for n in new_vals: | ||||
|                         rel_manager.add(f.rel.to._default_manager.get(pk=n)) | ||||
|                     # TODO: Add to 'fields_changed' | ||||
|  | ||||
|         expanded_data = DotExpandedDict(dict(new_data)) | ||||
|         # Save many-to-one objects. Example: Add the Choice objects for a Poll. | ||||
|         for related in self.opts.get_all_related_objects(): | ||||
|             # Create obj_list, which is a DotExpandedDict such as this: | ||||
|             # [('0', {'id': ['940'], 'choice': ['This is the first choice']}), | ||||
|             #  ('1', {'id': ['941'], 'choice': ['This is the second choice']}), | ||||
|             #  ('2', {'id': [''], 'choice': ['']})] | ||||
|             child_follow = self.follow.get(related.name, None) | ||||
|  | ||||
|             if child_follow: | ||||
|                 obj_list = expanded_data.get(related.var_name, {}).items() | ||||
|                 if not obj_list: | ||||
|                     continue | ||||
|  | ||||
|                 obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0]))) | ||||
|  | ||||
|                 # For each related item... | ||||
|                 for _, rel_new_data in obj_list: | ||||
|  | ||||
|                     params = {} | ||||
|  | ||||
|                     # Keep track of which core=True fields were provided. | ||||
|                     # If all core fields were given, the related object will be saved. | ||||
|                     # If none of the core fields were given, the object will be deleted. | ||||
|                     # If some, but not all, of the fields were given, the validator would | ||||
|                     # have caught that. | ||||
|                     all_cores_given, all_cores_blank = True, True | ||||
|  | ||||
|                     # Get a reference to the old object. We'll use it to compare the | ||||
|                     # old to the new, to see which fields have changed. | ||||
|                     old_rel_obj = None | ||||
|                     if self.change: | ||||
|                         if rel_new_data[related.opts.pk.name][0]: | ||||
|                             try: | ||||
|                                 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]}) | ||||
|                             except ObjectDoesNotExist: | ||||
|                                 pass | ||||
|  | ||||
|                     for f in related.opts.fields: | ||||
|                         if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''): | ||||
|                             all_cores_given = False | ||||
|                         elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''): | ||||
|                             all_cores_blank = False | ||||
|                         # If this field isn't editable, give it the same value it had | ||||
|                         # previously, according to the given ID. If the ID wasn't | ||||
|                         # given, use a default value. FileFields are also a special | ||||
|                         # case, because they'll be dealt with later. | ||||
|  | ||||
|                         if f == related.field: | ||||
|                             param = getattr(new_object, related.field.rel.get_related_field().attname) | ||||
|                         elif (not self.change) and isinstance(f, AutoField): | ||||
|                             param = None | ||||
|                         elif self.change and (isinstance(f, FileField) or not child_follow.get(f.name, None)): | ||||
|                             if old_rel_obj: | ||||
|                                 param = getattr(old_rel_obj, f.column) | ||||
|                             else: | ||||
|                                 param = f.get_default() | ||||
|                         else: | ||||
|                             param = f.get_manipulator_new_data(rel_new_data, rel=True) | ||||
|                         if param != None: | ||||
|                             params[f.attname] = param | ||||
|  | ||||
|                     # Create the related item. | ||||
|                     new_rel_obj = related.model(**params) | ||||
|  | ||||
|                     # If all the core fields were provided (non-empty), save the item. | ||||
|                     if all_cores_given: | ||||
|                         new_rel_obj.save() | ||||
|  | ||||
|                         # Save any uploaded files. | ||||
|                         for f in related.opts.fields: | ||||
|                             if child_follow.get(f.name, None): | ||||
|                                 if isinstance(f, FileField) and rel_new_data.get(f.name, False): | ||||
|                                     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) | ||||
|  | ||||
|                         # Calculate whether any fields have changed. | ||||
|                         if self.change: | ||||
|                             if not old_rel_obj: # This object didn't exist before. | ||||
|                                 self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj)) | ||||
|                             else: | ||||
|                                 for f in related.opts.fields: | ||||
|                                     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)): | ||||
|                                         self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj)) | ||||
|  | ||||
|                         # Save many-to-many objects. | ||||
|                         for f in related.opts.many_to_many: | ||||
|                             if child_follow.get(f.name, None) and not f.rel.edit_inline: | ||||
|                                 new_value = rel_new_data[f.attname] | ||||
|                                 setattr(new_rel_obj, f.name, f.rel.to.objects.filter(pk__in=new_value)) | ||||
|                                 if self.change: | ||||
|                                     self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj)) | ||||
|  | ||||
|                     # If, in the change stage, all of the core fields were blank and | ||||
|                     # the primary key (ID) was provided, delete the item. | ||||
|                     if self.change and all_cores_blank and old_rel_obj: | ||||
|                         new_rel_obj.delete() | ||||
|                         self.fields_deleted.append('%s "%s"' % (related.opts.verbose_name, old_rel_obj)) | ||||
|  | ||||
|         # Save the order, if applicable. | ||||
|         if self.change and self.opts.get_ordered_objects(): | ||||
|             order = new_data['order_'] and map(int, new_data['order_'].split(',')) or [] | ||||
|             for rel_opts in self.opts.get_ordered_objects(): | ||||
|                 getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) | ||||
|         return new_object | ||||
|  | ||||
|     def get_related_objects(self): | ||||
|         return self.opts.get_followed_related_objects(self.follow) | ||||
|  | ||||
|     def flatten_data(self): | ||||
|         new_data = {} | ||||
|         obj = self.change and self.original_object or None | ||||
|         for f in self.opts.get_data_holders(self.follow): | ||||
|             fol = self.follow.get(f.name) | ||||
|             new_data.update(f.flatten_data(fol, obj)) | ||||
|         return new_data | ||||
|  | ||||
| class AutomaticAddManipulator(AutomaticManipulator): | ||||
|     change = False | ||||
|  | ||||
| class AutomaticChangeManipulator(AutomaticManipulator): | ||||
|     change = True | ||||
|     def __init__(self, obj_key, follow=None): | ||||
|         self.obj_key = obj_key | ||||
|         try: | ||||
|             self.original_object = self.manager.get(pk=obj_key) | ||||
|         except ObjectDoesNotExist: | ||||
|             # If the object doesn't exist, this might be a manipulator for a | ||||
|             # one-to-one related object that hasn't created its subobject yet. | ||||
|             # For example, this might be a Restaurant for a Place that doesn't | ||||
|             # yet have restaurant information. | ||||
|             if self.opts.one_to_one_field: | ||||
|                 # Sanity check -- Make sure the "parent" object exists. | ||||
|                 # For example, make sure the Place exists for the Restaurant. | ||||
|                 # Let the ObjectDoesNotExist exception propagate up. | ||||
|                 limit_choices_to = self.opts.one_to_one_field.rel.limit_choices_to | ||||
|                 lookup_kwargs = {'%s__exact' % self.opts.one_to_one_field.rel.field_name: obj_key} | ||||
|                 self.opts.one_to_one_field.rel.to.get_model_module().complex_filter(limit_choices_to).get(**lookup_kwargs) | ||||
|                 params = dict([(f.attname, f.get_default()) for f in self.opts.fields]) | ||||
|                 params[self.opts.pk.attname] = obj_key | ||||
|                 self.original_object = self.opts.get_model_module().Klass(**params) | ||||
|             else: | ||||
|                 raise | ||||
|         super(AutomaticChangeManipulator, self).__init__(follow=follow) | ||||
|  | ||||
| def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): | ||||
|     from django.db.models.fields.related import ManyToOneRel | ||||
|     from django.utils.text import get_text_list | ||||
|     field_list = [opts.get_field(field_name) for field_name in field_name_list] | ||||
|     if isinstance(field_list[0].rel, ManyToOneRel): | ||||
|         kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data} | ||||
|     else: | ||||
|         kwargs = {'%s__iexact' % field_name_list[0]: field_data} | ||||
|     for f in field_list[1:]: | ||||
|         # This is really not going to work for fields that have different | ||||
|         # form fields, e.g. DateTime. | ||||
|         # This validation needs to occur after html2python to be effective. | ||||
|         field_val = all_data.get(f.name, None) | ||||
|         if field_val is None: | ||||
|             # This will be caught by another validator, assuming the field | ||||
|             # doesn't have blank=True. | ||||
|             return | ||||
|         if isinstance(f.rel, ManyToOneRel): | ||||
|             kwargs['%s__pk' % f.name] = field_val | ||||
|         else: | ||||
|             kwargs['%s__iexact' % f.name] = field_val | ||||
|     try: | ||||
|         old_obj = self.manager.get(**kwargs) | ||||
|     except ObjectDoesNotExist: | ||||
|         return | ||||
|     if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val(): | ||||
|         pass | ||||
|     else: | ||||
|         raise validators.ValidationError, _("%(object)s with this %(type)s already exists for the given %(field)s.") % \ | ||||
|             {'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'))} | ||||
|  | ||||
| def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data): | ||||
|     from django.db.models.fields.related import ManyToOneRel | ||||
|     date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None) | ||||
|     date_val = oldforms.DateField.html2python(date_str) | ||||
|     if date_val is None: | ||||
|         return # Date was invalid. This will be caught by another validator. | ||||
|     lookup_kwargs = {'%s__year' % date_field.name: date_val.year} | ||||
|     if isinstance(from_field.rel, ManyToOneRel): | ||||
|         lookup_kwargs['%s__pk' % from_field.name] = field_data | ||||
|     else: | ||||
|         lookup_kwargs['%s__iexact' % from_field.name] = field_data | ||||
|     if lookup_type in ('month', 'date'): | ||||
|         lookup_kwargs['%s__month' % date_field.name] = date_val.month | ||||
|     if lookup_type == 'date': | ||||
|         lookup_kwargs['%s__day' % date_field.name] = date_val.day | ||||
|     try: | ||||
|         old_obj = self.manager.get(**lookup_kwargs) | ||||
|     except ObjectDoesNotExist: | ||||
|         return | ||||
|     else: | ||||
|         if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val(): | ||||
|             pass | ||||
|         else: | ||||
|             format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y' | ||||
|             date_val = datetime_safe.new_datetime(date_val) | ||||
|             raise validators.ValidationError, "Please enter a different %s. The one you entered is already being used for %s." % \ | ||||
|                 (from_field.verbose_name, date_val.strftime(format_string)) | ||||
| @@ -396,28 +396,6 @@ class Options(object): | ||||
|             self._related_many_to_many_cache = cache | ||||
|         return cache | ||||
|  | ||||
|     def get_followed_related_objects(self, follow=None): | ||||
|         if follow == None: | ||||
|             follow = self.get_follow() | ||||
|         return [f for f in self.get_all_related_objects() if follow.get(f.name, None)] | ||||
|  | ||||
|     def get_data_holders(self, follow=None): | ||||
|         if follow == None: | ||||
|             follow = self.get_follow() | ||||
|         return [f for f in self.fields + self.many_to_many + self.get_all_related_objects() if follow.get(f.name, None)] | ||||
|  | ||||
|     def get_follow(self, override=None): | ||||
|         follow = {} | ||||
|         for f in self.fields + self.many_to_many + self.get_all_related_objects(): | ||||
|             if override and f.name in override: | ||||
|                 child_override = override[f.name] | ||||
|             else: | ||||
|                 child_override = None | ||||
|             fol = f.get_follow(child_override) | ||||
|             if fol != None: | ||||
|                 follow[f.name] = fol | ||||
|         return follow | ||||
|  | ||||
|     def get_base_chain(self, model): | ||||
|         """ | ||||
|         Returns a list of parent classes leading to 'model' (order from closet | ||||
| @@ -459,28 +437,3 @@ class Options(object): | ||||
|             #        objects.append(opts) | ||||
|             self._ordered_objects = objects | ||||
|         return self._ordered_objects | ||||
|  | ||||
|     def has_field_type(self, field_type, follow=None): | ||||
|         """ | ||||
|         Returns True if this object's admin form has at least one of the given | ||||
|         field_type (e.g. FileField). | ||||
|         """ | ||||
|         # TODO: follow | ||||
|         if not hasattr(self, '_field_types'): | ||||
|             self._field_types = {} | ||||
|         if field_type not in self._field_types: | ||||
|             try: | ||||
|                 # First check self.fields. | ||||
|                 for f in self.fields: | ||||
|                     if isinstance(f, field_type): | ||||
|                         raise StopIteration | ||||
|                 # Failing that, check related fields. | ||||
|                 for related in self.get_followed_related_objects(follow): | ||||
|                     for f in related.opts.fields: | ||||
|                         if isinstance(f, field_type): | ||||
|                             raise StopIteration | ||||
|             except StopIteration: | ||||
|                 self._field_types[field_type] = True | ||||
|             else: | ||||
|                 self._field_types[field_type] = False | ||||
|         return self._field_types[field_type] | ||||
|   | ||||
| @@ -15,113 +15,17 @@ class RelatedObject(object): | ||||
|         self.model = model | ||||
|         self.opts = model._meta | ||||
|         self.field = field | ||||
|         self.edit_inline = field.rel.edit_inline | ||||
|         self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name) | ||||
|         self.var_name = self.opts.object_name.lower() | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|         new_data = {} | ||||
|         rel_instances = self.get_list(obj) | ||||
|         for i, rel_instance in enumerate(rel_instances): | ||||
|             instance_data = {} | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 # TODO: Fix for recursive manipulators. | ||||
|                 fol = follow.get(f.name, None) | ||||
|                 if fol: | ||||
|                     field_data = f.flatten_data(fol, rel_instance) | ||||
|                     for name, value in field_data.items(): | ||||
|                         instance_data['%s.%d.%s' % (self.var_name, i, name)] = value | ||||
|             new_data.update(instance_data) | ||||
|         return new_data | ||||
|  | ||||
|     def extract_data(self, data): | ||||
|         """ | ||||
|         Pull out the data meant for inline objects of this class, | ||||
|         i.e. anything starting with our module name. | ||||
|         """ | ||||
|         return data # TODO | ||||
|  | ||||
|     def get_list(self, parent_instance=None): | ||||
|         "Get the list of this type of object from an instance of the parent class." | ||||
|         if parent_instance is not None: | ||||
|             attr = getattr(parent_instance, self.get_accessor_name()) | ||||
|             if self.field.rel.multiple: | ||||
|                 # For many-to-many relationships, return a list of objects | ||||
|                 # corresponding to the xxx_num_in_admin options of the field | ||||
|                 objects = list(attr.all()) | ||||
|  | ||||
|                 count = len(objects) + self.field.rel.num_extra_on_change | ||||
|                 if self.field.rel.min_num_in_admin: | ||||
|                     count = max(count, self.field.rel.min_num_in_admin) | ||||
|                 if self.field.rel.max_num_in_admin: | ||||
|                     count = min(count, self.field.rel.max_num_in_admin) | ||||
|  | ||||
|                 change = count - len(objects) | ||||
|                 if change > 0: | ||||
|                     return objects + [None] * change | ||||
|                 if change < 0: | ||||
|                     return objects[:change] | ||||
|                 else: # Just right | ||||
|                     return objects | ||||
|             else: | ||||
|                 # A one-to-one relationship, so just return the single related | ||||
|                 # object | ||||
|                 return [attr] | ||||
|         else: | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                 return [None] * max(self.field.rel.num_in_admin, self.field.rel.min_num_in_admin) | ||||
|             else: | ||||
|                 return [None] * self.field.rel.num_in_admin | ||||
|  | ||||
|     def get_db_prep_lookup(self, lookup_type, value): | ||||
|         # Defer to the actual field definition for db prep | ||||
|         return self.field.get_db_prep_lookup(lookup_type, value) | ||||
|          | ||||
|  | ||||
|     def editable_fields(self): | ||||
|         "Get the fields in this class that should be edited inline." | ||||
|         return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] | ||||
|  | ||||
|     def get_follow(self, override=None): | ||||
|         if isinstance(override, bool): | ||||
|             if override: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|         else: | ||||
|             if override: | ||||
|                 over = override.copy() | ||||
|             elif self.edit_inline: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|  | ||||
|         over[self.field.name] = False | ||||
|         return self.opts.get_follow(over) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, follow): | ||||
|         if self.field.rel.multiple: | ||||
|             if change: | ||||
|                 attr = getattr(manipulator.original_object, self.get_accessor_name()) | ||||
|                 count = attr.count() | ||||
|                 count += self.field.rel.num_extra_on_change | ||||
|             else: | ||||
|                 count = self.field.rel.num_in_admin | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                 count = max(count, self.field.rel.min_num_in_admin) | ||||
|             if self.field.rel.max_num_in_admin: | ||||
|                 count = min(count, self.field.rel.max_num_in_admin) | ||||
|         else: | ||||
|             count = 1 | ||||
|  | ||||
|         fields = [] | ||||
|         for i in range(count): | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 if follow.get(f.name, False): | ||||
|                     prefix = '%s.%d.' % (self.var_name, i) | ||||
|                     fields.extend(f.get_manipulator_fields(self.opts, manipulator, change, | ||||
|                                                            name_prefix=prefix, rel=True)) | ||||
|         return fields | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,7 @@ __all__ = ( | ||||
|     'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', | ||||
|     'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', | ||||
|     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', | ||||
|     'SplitDateTimeField', 'IPAddressField', 'FilePathField', | ||||
|     'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', | ||||
| ) | ||||
|  | ||||
| # These values, if given to to_python(), will trigger the self.required check. | ||||
| @@ -835,3 +835,14 @@ class IPAddressField(RegexField): | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs) | ||||
|  | ||||
| slug_re = re.compile(r'^[-\w]+$') | ||||
|  | ||||
| class SlugField(RegexField): | ||||
|     default_error_messages = { | ||||
|         'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers," | ||||
|                      u" underscores or hyphens."), | ||||
|     } | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super(SlugField, self).__init__(slug_re, *args, **kwargs) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -108,11 +108,11 @@ What does a field class do? | ||||
| All of Django's fields (and when we say *fields* in this document, we always | ||||
| mean model fields and not :ref:`form fields <ref-forms-fields>`) are subclasses | ||||
| of :class:`django.db.models.Field`. Most of the information that Django records | ||||
| about a field is common to all fields -- name, help text, validator lists, | ||||
| uniqueness and so forth. Storing all that information is handled by ``Field``. | ||||
| We'll get into the precise details of what ``Field`` can do later on; for now, | ||||
| suffice it to say that everything descends from ``Field`` and then customizes | ||||
| key pieces of the class behavior. | ||||
| about a field is common to all fields -- name, help text, uniqueness and so | ||||
| forth. Storing all that information is handled by ``Field``. We'll get into the | ||||
| precise details of what ``Field`` can do later on; for now, suffice it to say | ||||
| that everything descends from ``Field`` and then customizes key pieces of the | ||||
| class behavior. | ||||
|  | ||||
| It's important to realize that a Django field class is not what is stored in | ||||
| your model attributes. The model attributes contain normal Python objects. The | ||||
| @@ -210,7 +210,6 @@ parameters: | ||||
|     * :attr:`~django.db.models.Field.unique_for_date` | ||||
|     * :attr:`~django.db.models.Field.unique_for_month` | ||||
|     * :attr:`~django.db.models.Field.unique_for_year` | ||||
|     * :attr:`~django.db.models.Field.validator_list` | ||||
|     * :attr:`~django.db.models.Field.choices` | ||||
|     * :attr:`~django.db.models.Field.help_text` | ||||
|     * :attr:`~django.db.models.Field.db_column` | ||||
| @@ -567,33 +566,19 @@ output in some other place, outside of Django. | ||||
| Converting field data for serialization | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| .. method:: flatten_data(self, follow, obj=None) | ||||
|  | ||||
| .. admonition:: Subject to change | ||||
|  | ||||
|     Although implementing this method is necessary to allow field | ||||
|     serialization, the API might change in the future. | ||||
|  | ||||
| Returns a dictionary, mapping the field's attribute name to a flattened string | ||||
| version of the data. This method has some internal uses that aren't of interest | ||||
| to use here (mostly having to do with forms). For our purposes, it's sufficient | ||||
| to return a one item dictionary that maps the attribute name to a string. | ||||
| .. method:: value_to_string(self, obj) | ||||
|  | ||||
| This method is used by the serializers to convert the field into a string for | ||||
| output. You can ignore the input parameters for serialization purposes, although | ||||
| calling :meth:`Field._get_val_from_obj(obj) | ||||
| <django.db.models.Field._get_val_from_obj>` is the best way to get the value to | ||||
| serialize. | ||||
|  | ||||
| For example, since our ``HandField`` uses strings for its data storage anyway, | ||||
| we can reuse some existing conversion code:: | ||||
| output. Calling :meth:``Field._get_val_from_obj(obj)`` is the best way to get the | ||||
| value to serialize. For example, since our ``HandField`` uses strings for its | ||||
| data storage anyway, we can reuse some existing conversion code:: | ||||
|  | ||||
|     class HandField(models.Field): | ||||
|         # ... | ||||
|  | ||||
|         def flatten_data(self, follow, obj=None): | ||||
|         def value_to_string(self, obj): | ||||
|             value = self._get_val_from_obj(obj) | ||||
|             return {self.attname: self.get_db_prep_value(value)} | ||||
|             return self.get_db_prep_value(value) | ||||
|  | ||||
| Some general advice | ||||
| -------------------- | ||||
|   | ||||
| @@ -154,12 +154,12 @@ shell command: | ||||
|  | ||||
| One low-tech way of taking advantage of the text documentation is by using the | ||||
| Unix ``grep`` utility to search for a phrase in all of the documentation. For | ||||
| example, this will show you each mention of the phrase "edit_inline" in any | ||||
| example, this will show you each mention of the phrase "max_length" in any | ||||
| Django document: | ||||
|  | ||||
| .. code-block:: bash | ||||
|  | ||||
|     $ grep edit_inline /path/to/django/docs/*.txt | ||||
|     $ grep max_length /path/to/django/docs/*.txt | ||||
|      | ||||
| As HTML, locally | ||||
| ---------------- | ||||
|   | ||||
| @@ -1,692 +0,0 @@ | ||||
| .. _obsolete-forms: | ||||
|  | ||||
| =============================== | ||||
| Forms, fields, and manipulators | ||||
| =============================== | ||||
|  | ||||
| Forwards-compatibility note | ||||
| =========================== | ||||
|  | ||||
| The legacy forms/manipulators system described in this document is going to be | ||||
| replaced in the next Django release. If you're starting from scratch, we | ||||
| strongly encourage you not to waste your time learning this. Instead, learn and | ||||
| use the new :ref:`forms library <topics-forms-index>`. | ||||
|  | ||||
| Introduction | ||||
| ============ | ||||
|  | ||||
| Once you've got a chance to play with Django's admin interface, you'll probably | ||||
| wonder if the fantastic form validation framework it uses is available to user | ||||
| code. It is, and this document explains how the framework works. | ||||
|  | ||||
| We'll take a top-down approach to examining Django's form validation framework, | ||||
| because much of the time you won't need to use the lower-level APIs. Throughout | ||||
| this document, we'll be working with the following model, a "place" object:: | ||||
|  | ||||
|     from django.db import models | ||||
|  | ||||
|     PLACE_TYPES = ( | ||||
|         (1, 'Bar'), | ||||
|         (2, 'Restaurant'), | ||||
|         (3, 'Movie Theater'), | ||||
|         (4, 'Secret Hideout'), | ||||
|     ) | ||||
|  | ||||
|     class Place(models.Model): | ||||
|         name = models.CharField(max_length=100) | ||||
|         address = models.CharField(max_length=100, blank=True) | ||||
|         city = models.CharField(max_length=50, blank=True) | ||||
|         state = models.USStateField() | ||||
|         zip_code = models.CharField(max_length=5, blank=True) | ||||
|         place_type = models.IntegerField(choices=PLACE_TYPES) | ||||
|  | ||||
|         class Admin: | ||||
|             pass | ||||
|  | ||||
|         def __unicode__(self): | ||||
|             return self.name | ||||
|  | ||||
| Defining the above class is enough to create an admin interface to a ``Place``, | ||||
| but what if you want to allow public users to submit places? | ||||
|  | ||||
| Automatic Manipulators | ||||
| ====================== | ||||
|  | ||||
| The highest-level interface for object creation and modification is the | ||||
| **automatic Manipulator** framework. An automatic manipulator is a utility | ||||
| class tied to a given model that "knows" how to create or modify instances of | ||||
| that model and how to validate data for the object. Automatic Manipulators come | ||||
| in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally | ||||
| they are quite similar, but the former knows how to create new instances of the | ||||
| model, while the latter modifies existing instances. Both types of classes are | ||||
| automatically created when you define a new class:: | ||||
|  | ||||
|     >>> from mysite.myapp.models import Place | ||||
|     >>> Place.AddManipulator | ||||
|     <class 'django.models.manipulators.AddManipulator'> | ||||
|     >>> Place.ChangeManipulator | ||||
|     <class 'django.models.manipulators.ChangeManipulator'> | ||||
|  | ||||
| Using the ``AddManipulator`` | ||||
| ---------------------------- | ||||
|  | ||||
| We'll start with the ``AddManipulator``.  Here's a very simple view that takes | ||||
| POSTed data from the browser and creates a new ``Place`` object:: | ||||
|  | ||||
|     from django.shortcuts import render_to_response | ||||
|     from django.http import Http404, HttpResponse, HttpResponseRedirect | ||||
|     from django import oldforms as forms | ||||
|     from mysite.myapp.models import Place | ||||
|  | ||||
|     def naive_create_place(request): | ||||
|         """A naive approach to creating places; don't actually use this!""" | ||||
|         # Create the AddManipulator. | ||||
|         manipulator = Place.AddManipulator() | ||||
|  | ||||
|         # Make a copy of the POSTed data so that do_html2python can | ||||
|         # modify it in place (request.POST is immutable). | ||||
|         new_data = request.POST.copy() | ||||
|  | ||||
|         # Convert the request data (which will all be strings) into the | ||||
|         # appropriate Python types for those fields. | ||||
|         manipulator.do_html2python(new_data) | ||||
|  | ||||
|         # Save the new object. | ||||
|         new_place = manipulator.save(new_data) | ||||
|  | ||||
|         # It worked! | ||||
|         return HttpResponse("Place created: %s" % new_place) | ||||
|  | ||||
| The ``naive_create_place`` example works, but as you probably can tell, this | ||||
| view has a number of problems: | ||||
|  | ||||
|     * No validation of any sort is performed. If, for example, the ``name`` field | ||||
|       isn't given in ``request.POST``, the save step will cause a database error | ||||
|       because that field is required. Ugly. | ||||
|  | ||||
|     * Even if you *do* perform validation, there's still no way to give that | ||||
|       information to the user in any sort of useful way. | ||||
|  | ||||
|     * You'll have to separately create a form (and view) that submits to this | ||||
|       page, which is a pain and is redundant. | ||||
|  | ||||
| Let's dodge these problems momentarily to take a look at how you could create a | ||||
| view with a form that submits to this flawed creation view:: | ||||
|  | ||||
|     def naive_create_place_form(request): | ||||
|         """Simplistic place form view; don't actually use anything like this!""" | ||||
|         # Create a FormWrapper object that the template can use. Ignore | ||||
|         # the last two arguments to FormWrapper for now. | ||||
|         form = forms.FormWrapper(Place.AddManipulator(), {}, {}) | ||||
|         return render_to_response('places/naive_create_form.html', {'form': form}) | ||||
|  | ||||
| (This view, as well as all the following ones, has the same imports as in the | ||||
| first example above.) | ||||
|  | ||||
| The ``forms.FormWrapper`` object is a wrapper that templates can | ||||
| easily deal with to create forms. Here's the ``naive_create_form.html`` | ||||
| template:: | ||||
|  | ||||
|     {% extends "base.html" %} | ||||
|  | ||||
|     {% block content %} | ||||
|     <h1>Create a place:</h1> | ||||
|  | ||||
|     <form method="post" action="../do_new/"> | ||||
|     <p><label for="id_name">Name:</label> {{ form.name }}</p> | ||||
|     <p><label for="id_address">Address:</label> {{ form.address }}</p> | ||||
|     <p><label for="id_city">City:</label> {{ form.city }}</p> | ||||
|     <p><label for="id_state">State:</label> {{ form.state }}</p> | ||||
|     <p><label for="id_zip_code">Zip:</label> {{ form.zip_code }}</p> | ||||
|     <p><label for="id_place_type">Place type:</label> {{ form.place_type }}</p> | ||||
|     <input type="submit" /> | ||||
|     </form> | ||||
|     {% endblock %} | ||||
|  | ||||
| Before we get back to the problems with these naive set of views, let's go over | ||||
| some salient points of the above template: | ||||
|  | ||||
|     * Field "widgets" are handled for you: ``{{ form.field }}`` automatically | ||||
|       creates the "right" type of widget for the form, as you can see with the | ||||
|       ``place_type`` field above. | ||||
|  | ||||
|     * There isn't a way just to spit out the form. You'll still need to define | ||||
|       how the form gets laid out. This is a feature: Every form should be | ||||
|       designed differently. Django doesn't force you into any type of mold. | ||||
|       If you must use tables, use tables. If you're a semantic purist, you can | ||||
|       probably find better HTML than in the above template. | ||||
|  | ||||
|     * To avoid name conflicts, the ``id`` values of form elements take the | ||||
|       form "id_*fieldname*". | ||||
|  | ||||
| By creating a creation form we've solved problem number 3 above, but we still | ||||
| don't have any validation. Let's revise the validation issue by writing a new | ||||
| creation view that takes validation into account:: | ||||
|  | ||||
|     def create_place_with_validation(request): | ||||
|         manipulator = Place.AddManipulator() | ||||
|         new_data = request.POST.copy() | ||||
|  | ||||
|         # Check for validation errors | ||||
|         errors = manipulator.get_validation_errors(new_data) | ||||
|         manipulator.do_html2python(new_data) | ||||
|         if errors: | ||||
|             return render_to_response('places/errors.html', {'errors': errors}) | ||||
|         else: | ||||
|             new_place = manipulator.save(new_data) | ||||
|             return HttpResponse("Place created: %s" % new_place) | ||||
|  | ||||
| In this new version, errors will be found -- ``manipulator.get_validation_errors`` | ||||
| handles all the validation for you -- and those errors can be nicely presented | ||||
| on an error page (templated, of course):: | ||||
|  | ||||
|     {% extends "base.html" %} | ||||
|  | ||||
|     {% block content %} | ||||
|  | ||||
|     <h1>Please go back and correct the following error{{ errors|pluralize }}:</h1> | ||||
|     <ul> | ||||
|         {% for e in errors.items %} | ||||
|         <li>Field "{{ e.0 }}": {{ e.1|join:", " }}</li> | ||||
|         {% endfor %} | ||||
|     </ul> | ||||
|  | ||||
|     {% endblock %} | ||||
|  | ||||
| Still, this has its own problems: | ||||
|  | ||||
|     * There's still the issue of creating a separate (redundant) view for the | ||||
|       submission form. | ||||
|  | ||||
|     * Errors, though nicely presented, are on a separate page, so the user will | ||||
|       have to use the "back" button to fix errors. That's ridiculous and unusable. | ||||
|  | ||||
| The best way to deal with these issues is to collapse the two views -- the form | ||||
| and the submission -- into a single view.  This view will be responsible for | ||||
| creating the form, validating POSTed data, and creating the new object (if the | ||||
| data is valid). An added bonus of this approach is that errors and the form will | ||||
| both be available on the same page, so errors with fields can be presented in | ||||
| context. | ||||
|  | ||||
| .. admonition:: Philosophy: | ||||
|  | ||||
|     Finally, for the HTTP purists in the audience (and the authorship), this | ||||
|     nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches | ||||
|     the form, and POST creates the new object. | ||||
|  | ||||
| Below is the finished view:: | ||||
|  | ||||
|     def create_place(request): | ||||
|         manipulator = Place.AddManipulator() | ||||
|  | ||||
|         if request.method == 'POST': | ||||
|             # If data was POSTed, we're trying to create a new Place. | ||||
|             new_data = request.POST.copy() | ||||
|  | ||||
|             # Check for errors. | ||||
|             errors = manipulator.get_validation_errors(new_data) | ||||
|             manipulator.do_html2python(new_data) | ||||
|  | ||||
|             if not errors: | ||||
|                 # No errors. This means we can save the data! | ||||
|                 new_place = manipulator.save(new_data) | ||||
|  | ||||
|                 # Redirect to the object's "edit" page. Always use a redirect | ||||
|                 # after POST data, so that reloads don't accidentally create | ||||
|                 # duplicate entries, and so users don't see the confusing | ||||
|                 # "Repost POST data?" alert box in their browsers. | ||||
|                 return HttpResponseRedirect("/places/edit/%i/" % new_place.id) | ||||
|         else: | ||||
|             # No POST, so we want a brand new form without any data or errors. | ||||
|             errors = new_data = {} | ||||
|  | ||||
|         # Create the FormWrapper, template, context, response. | ||||
|         form = forms.FormWrapper(manipulator, new_data, errors) | ||||
|         return render_to_response('places/create_form.html', {'form': form}) | ||||
|  | ||||
| and here's the ``create_form`` template:: | ||||
|  | ||||
|     {% extends "base.html" %} | ||||
|  | ||||
|     {% block content %} | ||||
|     <h1>Create a place:</h1> | ||||
|  | ||||
|     {% if form.has_errors %} | ||||
|     <h2>Please correct the following error{{ form.error_dict|pluralize }}:</h2> | ||||
|     {% endif %} | ||||
|  | ||||
|     <form method="post" action="."> | ||||
|     <p> | ||||
|         <label for="id_name">Name:</label> {{ form.name }} | ||||
|         {% if form.name.errors %}*** {{ form.name.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|         <label for="id_address">Address:</label> {{ form.address }} | ||||
|         {% if form.address.errors %}*** {{ form.address.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|         <label for="id_city">City:</label> {{ form.city }} | ||||
|         {% if form.city.errors %}*** {{ form.city.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|         <label for="id_state">State:</label> {{ form.state }} | ||||
|         {% if form.state.errors %}*** {{ form.state.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|         <label for="id_zip_code">Zip:</label> {{ form.zip_code }} | ||||
|         {% if form.zip_code.errors %}*** {{ form.zip_code.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|         <label for="id_place_type">Place type:</label> {{ form.place_type }} | ||||
|         {% if form.place_type.errors %}*** {{ form.place_type.errors|join:", " }}{% endif %} | ||||
|     </p> | ||||
|     <input type="submit" /> | ||||
|     </form> | ||||
|     {% endblock %} | ||||
|  | ||||
| The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``) | ||||
| deserve some mention. | ||||
|  | ||||
| The first is any "default" data to be used as values for the fields. Pulling | ||||
| the data from ``request.POST``, as is done above, makes sure that if there are | ||||
| errors, the values the user put in aren't lost. If you try the above example, | ||||
| you'll see this in action. | ||||
|  | ||||
| The second argument is the error list retrieved from | ||||
| ``manipulator.get_validation_errors``.  When passed into the ``FormWrapper``, | ||||
| this gives each field an ``errors`` item (which is a list of error messages | ||||
| associated with the field) as well as a ``html_error_list`` item, which is a | ||||
| ``<ul>`` of error messages. The above template uses these error items to | ||||
| display a simple error message next to each field. The error list is saved as | ||||
| an ``error_dict`` attribute of the ``FormWrapper`` object. | ||||
|  | ||||
| Using the ``ChangeManipulator`` | ||||
| ------------------------------- | ||||
|  | ||||
| The above has covered using the ``AddManipulator`` to create a new object. What | ||||
| about editing an existing one? It's shockingly similar to creating a new one:: | ||||
|  | ||||
|     def edit_place(request, place_id): | ||||
|         # Get the place in question from the database and create a | ||||
|         # ChangeManipulator at the same time. | ||||
|         try: | ||||
|             manipulator = Place.ChangeManipulator(place_id) | ||||
|         except Place.DoesNotExist: | ||||
|             raise Http404 | ||||
|  | ||||
|         # Grab the Place object in question for future use. | ||||
|         place = manipulator.original_object | ||||
|  | ||||
|         if request.method == 'POST': | ||||
|             new_data = request.POST.copy() | ||||
|             errors = manipulator.get_validation_errors(new_data) | ||||
|             manipulator.do_html2python(new_data) | ||||
|             if not errors: | ||||
|                 manipulator.save(new_data) | ||||
|  | ||||
|                 # Do a post-after-redirect so that reload works, etc. | ||||
|                 return HttpResponseRedirect("/places/edit/%i/" % place.id) | ||||
|         else: | ||||
|             errors = {} | ||||
|             # This makes sure the form accurate represents the fields of the place. | ||||
|             new_data = manipulator.flatten_data() | ||||
|  | ||||
|         form = forms.FormWrapper(manipulator, new_data, errors) | ||||
|         return render_to_response('places/edit_form.html', {'form': form, 'place': place}) | ||||
|  | ||||
| The only real differences are: | ||||
|  | ||||
|     * We create a ``ChangeManipulator`` instead of an ``AddManipulator``. | ||||
|       The argument to a ``ChangeManipulator`` is the ID of the object | ||||
|       to be changed. As you can see, the initializer will raise an | ||||
|       ``ObjectDoesNotExist`` exception if the ID is invalid. | ||||
|  | ||||
|     * ``ChangeManipulator.original_object`` stores the instance of the | ||||
|       object being edited. | ||||
|  | ||||
|     * We set ``new_data`` based upon ``flatten_data()`` from the manipulator. | ||||
|       ``flatten_data()`` takes the data from the original object under | ||||
|       manipulation, and converts it into a data dictionary that can be used | ||||
|       to populate form elements with the existing values for the object. | ||||
|  | ||||
|     * The above example uses a different template, so create and edit can be | ||||
|       "skinned" differently if needed, but the form chunk itself is completely | ||||
|       identical to the one in the create form above. | ||||
|  | ||||
| The astute programmer will notice the add and create functions are nearly | ||||
| identical and could in fact be collapsed into a single view. This is left as an | ||||
| exercise for said programmer. | ||||
|  | ||||
| (However, the even-more-astute programmer will take heed of the note at the top | ||||
| of this document and check out the :ref:`generic views <ref-generic-views>` | ||||
| documentation if all she wishes to do is this type of simple create/update.) | ||||
|  | ||||
| Custom forms and manipulators | ||||
| ============================= | ||||
|  | ||||
| All the above is fine and dandy if you just want to use the automatically | ||||
| created manipulators. But the coolness doesn't end there: You can easily create | ||||
| your own custom manipulators for handling custom forms. | ||||
|  | ||||
| Custom manipulators are pretty simple. Here's a manipulator that you might use | ||||
| for a "contact" form on a website:: | ||||
|  | ||||
|     from django import oldforms as forms | ||||
|  | ||||
|     urgency_choices = ( | ||||
|         (1, "Extremely urgent"), | ||||
|         (2, "Urgent"), | ||||
|         (3, "Normal"), | ||||
|         (4, "Unimportant"), | ||||
|     ) | ||||
|  | ||||
|     class ContactManipulator(forms.Manipulator): | ||||
|         def __init__(self): | ||||
|             self.fields = ( | ||||
|                 forms.EmailField(field_name="from", is_required=True), | ||||
|                 forms.TextField(field_name="subject", length=30, max_length=200, is_required=True), | ||||
|                 forms.SelectField(field_name="urgency", choices=urgency_choices), | ||||
|                 forms.LargeTextField(field_name="contents", is_required=True), | ||||
|             ) | ||||
|  | ||||
| A certain similarity to Django's models should be apparent. The only required | ||||
| method of a custom manipulator is ``__init__`` which must define the fields | ||||
| present in the manipulator.  See the ``django.forms`` module for | ||||
| all the form fields provided by Django. | ||||
|  | ||||
| You use this custom manipulator exactly as you would use an auto-generated one. | ||||
| Here's a simple function that might drive the above form:: | ||||
|  | ||||
|     def contact_form(request): | ||||
|         manipulator = ContactManipulator() | ||||
|         if request.method == 'POST': | ||||
|             new_data = request.POST.copy() | ||||
|             errors = manipulator.get_validation_errors(new_data) | ||||
|             manipulator.do_html2python(new_data) | ||||
|             if not errors: | ||||
|  | ||||
|                 # Send e-mail using new_data here... | ||||
|  | ||||
|                 return HttpResponseRedirect("/contact/thankyou/") | ||||
|         else: | ||||
|             errors = new_data = {} | ||||
|         form = forms.FormWrapper(manipulator, new_data, errors) | ||||
|         return render_to_response('contact_form.html', {'form': form}) | ||||
|  | ||||
| Implementing ``flatten_data`` for custom manipulators | ||||
| ------------------------------------------------------ | ||||
|  | ||||
| It is possible (although rarely needed) to replace the default automatically | ||||
| created manipulators on a model with your own custom manipulators. If you do | ||||
| this and you are intending to use those models in generic views, you should | ||||
| also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement. | ||||
| This should act like the default ``flatten_data`` and return a dictionary | ||||
| mapping field names to their values, like so:: | ||||
|  | ||||
|     def flatten_data(self): | ||||
|         obj = self.original_object | ||||
|         return dict( | ||||
|             from = obj.from, | ||||
|             subject = obj.subject, | ||||
|             ... | ||||
|         ) | ||||
|  | ||||
| In this way, your new change manipulator will act exactly like the default | ||||
| version. | ||||
|  | ||||
| ``FileField`` and ``ImageField`` special cases | ||||
| ============================================== | ||||
|  | ||||
| Dealing with ``FileField`` and ``ImageField`` objects is a little more | ||||
| complicated. | ||||
|  | ||||
| First, you'll need to make sure that your ``<form>`` element correctly defines | ||||
| the ``enctype`` as ``"multipart/form-data"``, in order to upload files:: | ||||
|  | ||||
|   <form enctype="multipart/form-data" method="post" action="/foo/"> | ||||
|  | ||||
| Next, you'll need to treat the field in the template slightly differently. A | ||||
| ``FileField`` or ``ImageField`` is represented by *two* HTML form elements. | ||||
|  | ||||
| For example, given this field in a model:: | ||||
|  | ||||
|    photo = model.ImageField('/path/to/upload/location') | ||||
|  | ||||
| You'd need to display two formfields in the template:: | ||||
|  | ||||
|    <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p> | ||||
|  | ||||
| The first bit (``{{ form.photo }}``) displays the currently-selected file, | ||||
| while the second (``{{ form.photo_file }}``) actually contains the file upload | ||||
| form field. Thus, at the validation layer you need to check the ``photo_file`` | ||||
| key. | ||||
|  | ||||
| Finally, in your view, make sure to access ``request.FILES``, rather than | ||||
| ``request.POST``, for the uploaded files. This is necessary because | ||||
| ``request.POST`` does not contain file-upload data. | ||||
|  | ||||
| For example, following the ``new_data`` convention, you might do something like | ||||
| this:: | ||||
|  | ||||
|    new_data = request.POST.copy() | ||||
|    new_data.update(request.FILES) | ||||
|  | ||||
| Validators | ||||
| ========== | ||||
|  | ||||
| One useful feature of manipulators is the automatic validation. Validation is | ||||
| done using a simple validation API: A validator is a callable that raises a | ||||
| ``ValidationError`` if there's something wrong with the data. | ||||
| ``django.core.validators`` defines a host of validator functions (see below), | ||||
| but defining your own couldn't be easier:: | ||||
|  | ||||
|     from django.core import validators | ||||
|     from django import oldforms as forms | ||||
|  | ||||
|     class ContactManipulator(forms.Manipulator): | ||||
|         def __init__(self): | ||||
|             self.fields = ( | ||||
|                 # ... snip fields as above ... | ||||
|                 forms.EmailField(field_name="to", validator_list=[self.isValidToAddress]) | ||||
|             ) | ||||
|  | ||||
|         def isValidToAddress(self, field_data, all_data): | ||||
|             if not field_data.endswith("@example.com"): | ||||
|                 raise validators.ValidationError("You can only send messages to example.com e-mail addresses.") | ||||
|  | ||||
| Above, we've added a "to" field to the contact form, but required that the "to" | ||||
| address end with "@example.com" by adding the ``isValidToAddress`` validator to | ||||
| the field's ``validator_list``. | ||||
|  | ||||
| The arguments to a validator function take a little explanation.  ``field_data`` | ||||
| is the value of the field in question, and ``all_data`` is a dictionary of all | ||||
| the data being validated. | ||||
|  | ||||
| .. admonition:: Note:: | ||||
|  | ||||
|     At the point validators are called all data will still be | ||||
|     strings (as ``do_html2python`` hasn't been called yet). | ||||
|  | ||||
| Also, because consistency in user interfaces is important, we strongly urge you | ||||
| to put punctuation at the end of your validation messages. | ||||
|  | ||||
| When are validators called? | ||||
| --------------------------- | ||||
|  | ||||
| After a form has been submitted, Django validates each field in turn. First, | ||||
| if the field is required, Django checks that it is present and non-empty. Then, | ||||
| if that test passes *and the form submission contained data* for that field, all | ||||
| the validators for that field are called in turn. The emphasized portion in the | ||||
| last sentence is important: if a form field is not submitted (because it | ||||
| contains no data -- which is normal HTML behavior), the validators are not | ||||
| run against the field. | ||||
|  | ||||
| This feature is particularly important for models using | ||||
| ``models.BooleanField`` or custom manipulators using things like | ||||
| ``forms.CheckBoxField``. If the checkbox is not selected, it will not | ||||
| contribute to the form submission. | ||||
|  | ||||
| If you would like your validator to run *always*, regardless of whether its | ||||
| attached field contains any data, set the ``always_test`` attribute on the | ||||
| validator function. For example:: | ||||
|  | ||||
|     def my_custom_validator(field_data, all_data): | ||||
|         # ... | ||||
|     my_custom_validator.always_test = True | ||||
|  | ||||
| This validator will always be executed for any field it is attached to. | ||||
|  | ||||
| Ready-made validators | ||||
| --------------------- | ||||
|  | ||||
| Writing your own validator is not difficult, but there are some situations | ||||
| that come up over and over again. Django comes with a number of validators | ||||
| that can be used directly in your code. All of these functions and classes | ||||
| reside in ``django/core/validators.py``. | ||||
|  | ||||
| The following validators should all be self-explanatory. Each one provides a | ||||
| check for the given property: | ||||
|  | ||||
|     * isAlphaNumeric | ||||
|     * isAlphaNumericURL | ||||
|     * isSlug | ||||
|     * isLowerCase | ||||
|     * isUpperCase | ||||
|     * isCommaSeparatedIntegerList | ||||
|     * isCommaSeparatedEmailList | ||||
|     * isValidIPAddress4 | ||||
|     * isNotEmpty | ||||
|     * isOnlyDigits | ||||
|     * isNotOnlyDigits | ||||
|     * isInteger | ||||
|     * isOnlyLetters | ||||
|     * isValidANSIDate | ||||
|     * isValidANSITime | ||||
|     * isValidEmail | ||||
|     * isValidFloat | ||||
|     * isValidImage | ||||
|     * isValidImageURL | ||||
|     * isValidPhone | ||||
|     * isValidQuicktimeVideoURL | ||||
|     * isValidURL | ||||
|     * isValidHTML | ||||
|     * isWellFormedXml | ||||
|     * isWellFormedXmlFragment | ||||
|     * isExistingURL | ||||
|     * isValidUSState | ||||
|     * hasNoProfanities | ||||
|  | ||||
| There are also a group of validators that are slightly more flexible. For | ||||
| these validators, you create a validator instance, passing in the parameters | ||||
| described below. The returned object is a callable that can be used as a | ||||
| validator. | ||||
|  | ||||
| For example:: | ||||
|  | ||||
|     from django.core import validators | ||||
|     from django import oldforms as forms | ||||
|  | ||||
|     power_validator = validators.IsAPowerOf(2) | ||||
|  | ||||
|     class InstallationManipulator(forms.Manipulator) | ||||
|         def __init__(self): | ||||
|             self.fields = ( | ||||
|                 ... | ||||
|                 forms.IntegerField(field_name = "size", validator_list=[power_validator]) | ||||
|             ) | ||||
|  | ||||
| Here, ``validators.IsAPowerOf(...)`` returned something that could be used as | ||||
| a validator (in this case, a check that a number was a power of 2). | ||||
|  | ||||
| Each of the standard validators that take parameters have an optional final | ||||
| argument (``error_message``) that is the message returned when validation | ||||
| fails. If no message is passed in, a default message is used. | ||||
|  | ||||
| ``AlwaysMatchesOtherField`` | ||||
|     Takes a field name and the current field is valid if and only if its value | ||||
|     matches the contents of the other field. | ||||
|  | ||||
| ``ValidateIfOtherFieldEquals`` | ||||
|     Takes three parameters: ``other_field``, ``other_value`` and | ||||
|     ``validator_list``, in that order. If ``other_field`` has a value of | ||||
|     ``other_value``, then the validators in ``validator_list`` are all run | ||||
|     against the current field. | ||||
|  | ||||
| ``RequiredIfOtherFieldGiven`` | ||||
|     Takes a field name of the current field is only required if the other | ||||
|     field has a value. | ||||
|  | ||||
| ``RequiredIfOtherFieldsGiven`` | ||||
|     Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of | ||||
|     field names and if any one of the supplied fields has a value provided, | ||||
|     the current field being validated is required. | ||||
|  | ||||
| ``RequiredIfOtherFieldNotGiven`` | ||||
|     Takes the name of the other field and this field is only required if the | ||||
|     other field has no value. | ||||
|  | ||||
| ``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual`` | ||||
|     Each of these validator classes takes a field name and a value (in that | ||||
|     order). If the given field does (or does not have, in the latter case) the | ||||
|     given value, then the current field being validated is required. | ||||
|  | ||||
|     An optional ``other_label`` argument can be passed which, if given, is used | ||||
|     in error messages instead of the value. This allows more user friendly error | ||||
|     messages if the value itself is not descriptive enough. | ||||
|  | ||||
|     Note that because validators are called before any ``do_html2python()`` | ||||
|     functions, the value being compared against is a string. So | ||||
|     ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst | ||||
|     ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the | ||||
|     equality test succeeding. | ||||
|  | ||||
| ``IsLessThanOtherField`` | ||||
|     Takes a field name and validates that the current field being validated | ||||
|     has a value that is less than (or equal to) the other field's value. | ||||
|     Again, comparisons are done using strings, so be cautious about using | ||||
|     this function to compare data that should be treated as another type. The | ||||
|     string "123" is less than the string "2", for example. If you don't want | ||||
|     string comparison here, you will need to write your own validator. | ||||
|  | ||||
| ``NumberIsInRange`` | ||||
|     Takes two boundary numbers, ``lower`` and ``upper``, and checks that the | ||||
|     field is greater than ``lower`` (if given) and less than ``upper`` (if | ||||
|     given). | ||||
|  | ||||
|     Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow | ||||
|     values of both 10 and 20. This validator only checks numeric values | ||||
|     (e.g., float and integer values). | ||||
|  | ||||
| ``IsAPowerOf`` | ||||
|     Takes an integer argument and when called as a validator, checks that the | ||||
|     field being validated is a power of the integer. | ||||
|  | ||||
| ``IsValidDecimal`` | ||||
|     Takes a maximum number of digits and number of decimal places (in that | ||||
|     order) and validates whether the field is a decimal with no more than the | ||||
|     maximum number of digits and decimal places. | ||||
|  | ||||
| ``MatchesRegularExpression`` | ||||
|     Takes a regular expression (a string) as a parameter and validates the | ||||
|     field value against it. | ||||
|  | ||||
| ``AnyValidator`` | ||||
|     Takes a list of validators as a parameter. At validation time, if the | ||||
|     field successfully validates against any one of the validators, it passes | ||||
|     validation. The validators are tested in the order specified in the | ||||
|     original list. | ||||
|  | ||||
| ``URLMimeTypeCheck`` | ||||
|     Used to validate URL fields. Takes a list of MIME types (such as | ||||
|     ``text/plain``) at creation time. At validation time, it verifies that the | ||||
|     field is indeed a URL and then tries to retrieve the content at the URL. | ||||
|     Validation succeeds if the content could be retrieved and it has a content | ||||
|     type from the list used to create the validator. | ||||
|  | ||||
| ``RelaxNGCompact`` | ||||
|     Used to validate an XML document against a Relax NG compact schema. Takes a | ||||
|     file path to the location of the schema and an optional root element (which | ||||
|     is wrapped around the XML fragment before validation, if supplied). At | ||||
|     validation time, the XML fragment is validated against the schema using the | ||||
|     executable specified in the ``JING_PATH`` setting (see the :ref:`settings | ||||
|     <ref-settings>` document for more details). | ||||
| @@ -1,48 +0,0 @@ | ||||
| .. _howto-newforms-migration: | ||||
|  | ||||
| Migrating from "oldforms" to "newforms" | ||||
| ======================================= | ||||
|  | ||||
| :mod:`django.newforms` is new in Django's 0.96 release, but, as it won't be new | ||||
| forever. We plan to rename it to ``django.forms`` in next official release. The | ||||
| current ``django.forms`` package will be available as ``django.oldforms`` until | ||||
| Django 1.0, when we plan to remove it for good. | ||||
|  | ||||
| If you're using "old" forms -- and if you started using Django after 0.96 you're | ||||
| probably not -- you need to read this document and understand this migration | ||||
| plan. | ||||
|  | ||||
|     * The old forms framework (the current ``django.forms``) has been copied to | ||||
|       ``django.oldforms``. Thus, you can start upgrading your code *now*, | ||||
|       rather than waiting for the future backwards-incompatible change, by | ||||
|       changing your import statements like this:: | ||||
|  | ||||
|           from django import forms             # old | ||||
|           from django import oldforms as forms # new | ||||
|  | ||||
|     * In the next Django release, we will move the current ``django.newforms`` | ||||
|       to ``django.forms``. This will be a backwards-incompatible change, and | ||||
|       anybody who is still using the old version of ``django.forms`` at that | ||||
|       time will need to change their import statements, as described in the | ||||
|       previous bullet. | ||||
|  | ||||
|     * We will remove ``django.oldforms`` in Django 1.0. It will continue to be | ||||
|       available from older tags in our SVN repository, but it will not be | ||||
|       consider part of Django, and will not be supported.. | ||||
|  | ||||
| With this in mind, we recommend you use the following import statement when | ||||
| using ``django.newforms``:: | ||||
|  | ||||
|     from django import newforms as forms | ||||
|  | ||||
| This way, your code can refer to the ``forms`` module, and when | ||||
| ``django.newforms`` is renamed to ``django.forms``, you'll only have to change | ||||
| your ``import`` statements. | ||||
|  | ||||
| If you prefer "``import *``" syntax, you can do the following:: | ||||
|  | ||||
|     from django.newforms import * | ||||
|  | ||||
| This will import all fields, widgets, form classes and other various utilities | ||||
| into your local namespace. Some people find this convenient; others find it | ||||
| too messy. The choice is yours. | ||||
| @@ -33,11 +33,6 @@ exception or returns the clean value:: | ||||
|     ... | ||||
|     ValidationError: [u'Enter a valid e-mail address.'] | ||||
|  | ||||
| If you've used Django's old forms/validation framework, take care in noticing | ||||
| this ``ValidationError`` is different than the previous ``ValidationError``. | ||||
| This one lives at ``django.forms.ValidationError`` rather than | ||||
| ``django.core.validators.ValidationError``. | ||||
|  | ||||
| Core field arguments | ||||
| -------------------- | ||||
|  | ||||
|   | ||||
| @@ -145,23 +145,6 @@ hacking :attr:`~Field.choices` to be dynamic, you're probably better off using a | ||||
| proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is | ||||
| meant for static data that doesn't change much, if ever. | ||||
|  | ||||
| ``core`` | ||||
| -------- | ||||
|  | ||||
| .. attribute:: Field.core | ||||
|  | ||||
| For objects that are edited inline to a related object. | ||||
|  | ||||
| In the Django admin, if all "core" fields in an inline-edited object are | ||||
| cleared, the object will be deleted. | ||||
|  | ||||
| It is an error to have an inline-editable relation without at least one | ||||
| ``core=True`` field. | ||||
|  | ||||
| Please note that each field marked "core" is treated as a required field by the | ||||
| Django admin site. Essentially, this means you should put ``core=True`` on all | ||||
| required fields in your related object that is being edited inline. | ||||
|  | ||||
| ``db_column`` | ||||
| ------------- | ||||
|  | ||||
| @@ -287,15 +270,6 @@ respect to the month. | ||||
|  | ||||
| Like :attr:`~Field.unique_for_date` and :attr:`~Field.unique_for_month`. | ||||
|  | ||||
| ``validator_list`` | ||||
| ------------------ | ||||
|  | ||||
| .. attribute:: Field.validator_list | ||||
|  | ||||
| A list of extra validators to apply to the field. Each should be a callable that | ||||
| takes the parameters ``field_data, all_data`` and raises | ||||
| :exc:`django.core.validators.ValidationError` for errors. | ||||
|  | ||||
| .. _model-field-types: | ||||
|  | ||||
| Field types | ||||
| @@ -913,5 +887,4 @@ that control how the relationship functions. | ||||
|  | ||||
| The semantics of one-to-one relationships will be changing soon, so we don't | ||||
| recommend you use them. If that doesn't scare you away, however, | ||||
| :class:`OneToOneField` takes the same options that :class:`ForeignKey` does, | ||||
| except for the various :attr:`~ForeignKey.edit_inline`-related options. | ||||
| :class:`OneToOneField` takes the same options that :class:`ForeignKey` does. | ||||
|   | ||||
| @@ -67,8 +67,7 @@ the full list of conversions: | ||||
|                                      (from ``django.contrib.localflavor.us``) | ||||
|     ``PositiveIntegerField``         ``IntegerField`` | ||||
|     ``PositiveSmallIntegerField``    ``IntegerField`` | ||||
|     ``SlugField``                    ``RegexField`` accepting only letters, | ||||
|                                      numbers, underscores and hyphens | ||||
|     ``SlugField``                    ``SlugField`` | ||||
|     ``SmallIntegerField``            ``IntegerField`` | ||||
|     ``TextField``                    ``CharField`` with ``widget=Textarea`` | ||||
|     ``TimeField``                    ``TimeField`` | ||||
|   | ||||
| @@ -899,8 +899,7 @@ applications: | ||||
|     rendered on the form. | ||||
|  | ||||
|     ``form`` is the name the ``Form`` instance was given in the template | ||||
|     context. Note that this works only for ``forms.Form`` instances, not | ||||
|     ``oldforms.Form`` instances. | ||||
|     context. | ||||
|  | ||||
|     ``field`` is the name of the field on the form to check. If ``field`` | ||||
|     has a value of ``None``, non-field errors (errors you can access via | ||||
|   | ||||
| @@ -53,9 +53,6 @@ class SmallField(models.Field): | ||||
|             return [] | ||||
|         raise FieldError('Invalid lookup type: %r' % lookup_type) | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|         return {self.attname: force_unicode(self._get_val_from_obj(obj))} | ||||
|  | ||||
| class MyModel(models.Model): | ||||
|     name = models.CharField(max_length=10) | ||||
|     data = SmallField('small field') | ||||
|   | ||||
| @@ -23,13 +23,13 @@ class Target(models.Model): | ||||
|     clash1_set = models.CharField(max_length=10) | ||||
|  | ||||
| class Clash1(models.Model): | ||||
|     src_safe = models.CharField(max_length=10, core=True) | ||||
|     src_safe = models.CharField(max_length=10) | ||||
|  | ||||
|     foreign = models.ForeignKey(Target) | ||||
|     m2m = models.ManyToManyField(Target) | ||||
|  | ||||
| class Clash2(models.Model): | ||||
|     src_safe = models.CharField(max_length=10, core=True) | ||||
|     src_safe = models.CharField(max_length=10) | ||||
|  | ||||
|     foreign_1 = models.ForeignKey(Target, related_name='id') | ||||
|     foreign_2 = models.ForeignKey(Target, related_name='src_safe') | ||||
| @@ -46,7 +46,7 @@ class Target2(models.Model): | ||||
|     clashm2m_set = models.ManyToManyField(Target) | ||||
|  | ||||
| class Clash3(models.Model): | ||||
|     src_safe = models.CharField(max_length=10, core=True) | ||||
|     src_safe = models.CharField(max_length=10) | ||||
|  | ||||
|     foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt') | ||||
|     foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt') | ||||
| @@ -61,7 +61,7 @@ class ClashM2M(models.Model): | ||||
|     m2m = models.ManyToManyField(Target2) | ||||
|  | ||||
| class SelfClashForeign(models.Model): | ||||
|     src_safe = models.CharField(max_length=10, core=True) | ||||
|     src_safe = models.CharField(max_length=10) | ||||
|     selfclashforeign = models.CharField(max_length=10) | ||||
|  | ||||
|     selfclashforeign_set = models.ForeignKey("SelfClashForeign") | ||||
|   | ||||
| @@ -1,105 +0,0 @@ | ||||
| # coding: utf-8 | ||||
| """ | ||||
| 27. Default manipulators | ||||
|  | ||||
| Each model gets an ``AddManipulator`` and ``ChangeManipulator`` by default. | ||||
| """ | ||||
|  | ||||
| from django.db import models | ||||
|  | ||||
| class Musician(models.Model): | ||||
|     first_name = models.CharField(max_length=30) | ||||
|     last_name = models.CharField(max_length=30) | ||||
|  | ||||
|     def __unicode__(self): | ||||
|         return u"%s %s" % (self.first_name, self.last_name) | ||||
|  | ||||
| class Album(models.Model): | ||||
|     name = models.CharField(max_length=100) | ||||
|     musician = models.ForeignKey(Musician) | ||||
|     release_date = models.DateField(blank=True, null=True) | ||||
|  | ||||
|     def __unicode__(self): | ||||
|         return self.name | ||||
|  | ||||
| __test__ = {'API_TESTS':u""" | ||||
| >>> from django.utils.datastructures import MultiValueDict | ||||
|  | ||||
| # Create a Musician object via the default AddManipulator. | ||||
| >>> man = Musician.AddManipulator() | ||||
| >>> data = MultiValueDict({'first_name': ['Ella'], 'last_name': ['Fitzgerald']}) | ||||
|  | ||||
| >>> man.get_validation_errors(data) | ||||
| {} | ||||
| >>> man.do_html2python(data) | ||||
| >>> m1 = man.save(data) | ||||
|  | ||||
| # Verify it worked. | ||||
| >>> Musician.objects.all() | ||||
| [<Musician: Ella Fitzgerald>] | ||||
| >>> [m1] == list(Musician.objects.all()) | ||||
| True | ||||
|  | ||||
| # Attempt to add a Musician without a first_name. | ||||
| >>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name'] | ||||
| [u'This field is required.'] | ||||
|  | ||||
| # Attempt to add a Musician without a first_name and last_name. | ||||
| >>> errors = man.get_validation_errors(MultiValueDict({})) | ||||
| >>> errors['first_name'] | ||||
| [u'This field is required.'] | ||||
| >>> errors['last_name'] | ||||
| [u'This field is required.'] | ||||
|  | ||||
| # Attempt to create an Album without a name or musician. | ||||
| >>> man = Album.AddManipulator() | ||||
| >>> errors = man.get_validation_errors(MultiValueDict({})) | ||||
| >>> errors['musician'] | ||||
| [u'This field is required.'] | ||||
| >>> errors['name'] | ||||
| [u'This field is required.'] | ||||
|  | ||||
| # Attempt to create an Album with an invalid musician. | ||||
| >>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']})) | ||||
| >>> errors['musician'] | ||||
| [u"Select a valid choice; 'foo' is not in [u'', u'1']."] | ||||
|  | ||||
| # Attempt to create an Album with an invalid release_date. | ||||
| >>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'})) | ||||
| >>> errors['release_date'] | ||||
| [u'Enter a valid date in YYYY-MM-DD format.'] | ||||
|  | ||||
| # Create an Album without a release_date (because it's optional). | ||||
| >>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']}) | ||||
| >>> man.get_validation_errors(data) | ||||
| {} | ||||
| >>> man.do_html2python(data) | ||||
| >>> a1 = man.save(data) | ||||
|  | ||||
| # Verify it worked. | ||||
| >>> Album.objects.all() | ||||
| [<Album: Ella and Basie>] | ||||
| >>> Album.objects.get().musician | ||||
| <Musician: Ella Fitzgerald> | ||||
|  | ||||
| # Create an Album with a release_date. | ||||
| >>> data = MultiValueDict({'name': ['Ultimate Ella'], 'musician': ['1'], 'release_date': ['2005-02-13']}) | ||||
| >>> man.get_validation_errors(data) | ||||
| {} | ||||
| >>> man.do_html2python(data) | ||||
| >>> a2 = man.save(data) | ||||
|  | ||||
| # Verify it worked. | ||||
| >>> Album.objects.order_by('name') | ||||
| [<Album: Ella and Basie>, <Album: Ultimate Ella>] | ||||
| >>> a2 = Album.objects.get(pk=2) | ||||
| >>> a2 | ||||
| <Album: Ultimate Ella> | ||||
| >>> a2.release_date | ||||
| datetime.date(2005, 2, 13) | ||||
|  | ||||
| # Test isValidFloat Unicode coercion | ||||
| >>> from django.core.validators import isValidFloat, ValidationError | ||||
| >>> try: isValidFloat(u"ä", None) | ||||
| ... except ValidationError: pass | ||||
| """} | ||||
| @@ -7,7 +7,7 @@ Strings can be used instead of model literals to set up "lazy" relations. | ||||
| from django.db.models import * | ||||
|  | ||||
| class Parent(Model): | ||||
|     name = CharField(max_length=100, core=True) | ||||
|     name = CharField(max_length=100) | ||||
|      | ||||
|     # Use a simple string for forward declarations. | ||||
|     bestchild = ForeignKey("Child", null=True, related_name="favoured_by") | ||||
|   | ||||
| @@ -18,7 +18,7 @@ True | ||||
| >>> f.to_python("abc") | ||||
| Traceback (most recent call last): | ||||
| ... | ||||
| ValidationError: [u'This value must be a decimal number.'] | ||||
| ValidationError: This value must be a decimal number. | ||||
|  | ||||
| >>> f = DecimalField(max_digits=5, decimal_places=1) | ||||
| >>> x = f.to_python(2) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user