summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Wilson Jr <gary.wilson@gmail.com>2008-08-27 07:19:44 +0000
committerGary Wilson Jr <gary.wilson@gmail.com>2008-08-27 07:19:44 +0000
commitc2ba59fc1da5287d6286e2c2aca4083d5bafe056 (patch)
tree26c05bc1b845efadd28126adee8f2a3726f09424
parenta1575766604205b3bddf0f05d13ad698c78a7582 (diff)
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
-rw-r--r--django/contrib/admin/util.py6
-rw-r--r--django/contrib/auth/management/commands/createsuperuser.py19
-rw-r--r--django/contrib/auth/models.py1
-rw-r--r--django/contrib/comments/forms.py3
-rw-r--r--django/contrib/comments/models.py2
-rw-r--r--django/contrib/contenttypes/generic.py21
-rw-r--r--django/contrib/flatpages/models.py1
-rw-r--r--django/contrib/localflavor/jp/forms.py1
-rw-r--r--django/core/exceptions.py3
-rw-r--r--django/core/serializers/base.py7
-rw-r--r--django/core/validators.py598
-rw-r--r--django/db/models/__init__.py3
-rw-r--r--django/db/models/base.py8
-rw-r--r--django/db/models/fields/__init__.py313
-rw-r--r--django/db/models/fields/files.py54
-rw-r--r--django/db/models/fields/related.py98
-rw-r--r--django/db/models/manipulators.py333
-rw-r--r--django/db/models/options.py47
-rw-r--r--django/db/models/related.py98
-rw-r--r--django/forms/fields.py13
-rw-r--r--django/oldforms/__init__.py1056
-rw-r--r--docs/howto/custom-model-fields.txt37
-rw-r--r--docs/intro/whatsnext.txt4
-rw-r--r--docs/obsolete/forms.txt692
-rw-r--r--docs/obsolete/newforms-migration.txt48
-rw-r--r--docs/ref/forms/fields.txt5
-rw-r--r--docs/ref/models/fields.txt29
-rw-r--r--docs/topics/forms/modelforms.txt3
-rw-r--r--docs/topics/testing.txt3
-rw-r--r--tests/modeltests/field_subclassing/models.py3
-rw-r--r--tests/modeltests/invalid_models/models.py8
-rw-r--r--tests/modeltests/manipulators/__init__.py0
-rw-r--r--tests/modeltests/manipulators/models.py105
-rw-r--r--tests/modeltests/mutually_referential/models.py2
-rw-r--r--tests/regressiontests/model_fields/tests.py2
35 files changed, 158 insertions, 3468 deletions
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
index 0b6dbf2e6b..17213ea5b3 100644
--- a/django/contrib/admin/util.py
+++ b/django/contrib/admin/util.py
@@ -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') % \
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
index 91e39f7235..aaedcab0f4 100644
--- a/django/contrib/auth/management/commands/createsuperuser.py
+++ b/django/contrib/auth/management/commands/createsuperuser.py
@@ -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:
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 1bddbc2296..4d3189b08b 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -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
diff --git a/django/contrib/comments/forms.py b/django/contrib/comments/forms.py
index 4e7870501e..adb2741873 100644
--- a/django/contrib/comments/forms.py
+++ b/django/contrib/comments/forms.py
@@ -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
diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py
index 7424ec041a..40243d000d 100644
--- a/django/contrib/comments/models.py
+++ b/django/contrib/comments/models.py
@@ -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
diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index 9b6c15a22a..045b1ebab7 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -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'
-
diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py
index 02005dd07d..85873ac7b1 100644
--- a/django/contrib/flatpages/models.py
+++ b/django/contrib/flatpages/models.py
@@ -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 _
diff --git a/django/contrib/localflavor/jp/forms.py b/django/contrib/localflavor/jp/forms.py
index c86dbaf8da..1d765c8b17 100644
--- a/django/contrib/localflavor/jp/forms.py
+++ b/django/contrib/localflavor/jp/forms.py
@@ -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
diff --git a/django/core/exceptions.py b/django/core/exceptions.py
index e5df8caca8..1c21031739 100644
--- a/django/core/exceptions.py
+++ b/django/core/exceptions.py
@@ -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
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index bfd785a6fe..22de2d70d0 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -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):
"""
diff --git a/django/core/validators.py b/django/core/validators.py
deleted file mode 100644
index f94db40c1b..0000000000
--- a/django/core/validators.py
+++ /dev/null
@@ -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
diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
index cbd685547e..5413133306 100644
--- a/django/db/models/__init__.py
+++ b/django/db/models/__init__.py
@@ -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.
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 115d82bd4f..66cfb15be7 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -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:
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index b8d05671b1..5c21e7db73 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -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)]
-
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 935dee6162..f83f0cf575 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -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)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 7601911f0c..db606e5042 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -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)
diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py
deleted file mode 100644
index c657d0158b..0000000000
--- a/django/db/models/manipulators.py
+++ /dev/null
@@ -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))
diff --git a/django/db/models/options.py b/django/db/models/options.py
index ffea6d5082..3648cf0710 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -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]
diff --git a/django/db/models/related.py b/django/db/models/related.py
index 2c1dc5c516..ff7c787a93 100644
--- a/django/db/models/related.py
+++ b/django/db/models/related.py
@@ -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)
diff --git a/django/forms/fields.py b/django/forms/fields.py
index ee9b8c62f1..afbf6146b0 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -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)
diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py
deleted file mode 100644
index b5698ab807..0000000000
--- a/django/oldforms/__init__.py
+++ /dev/null
@@ -1,1056 +0,0 @@
-from django.core import validators
-from django.core.exceptions import PermissionDenied
-from django.utils.html import escape
-from django.utils.safestring import mark_safe
-from django.conf import settings
-from django.utils.translation import ugettext, ungettext
-from django.utils.encoding import smart_unicode, force_unicode
-
-FORM_FIELD_ID_PREFIX = 'id_'
-
-class EmptyValue(Exception):
- "This is raised when empty data is provided"
- pass
-
-class Manipulator(object):
- # List of permission strings. User must have at least one to manipulate.
- # None means everybody has permission.
- required_permission = ''
-
- def __init__(self):
- # List of FormField objects
- self.fields = []
-
- def __getitem__(self, field_name):
- "Looks up field by field name; raises KeyError on failure"
- for field in self.fields:
- if field.field_name == field_name:
- return field
- raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields))
-
- def __delitem__(self, field_name):
- "Deletes the field with the given field name; raises KeyError on failure"
- for i, field in enumerate(self.fields):
- if field.field_name == field_name:
- del self.fields[i]
- return
- raise KeyError, "Field %s not found" % field_name
-
- def check_permissions(self, user):
- """Confirms user has required permissions to use this manipulator; raises
- PermissionDenied on failure."""
- if self.required_permission is None:
- return
- if user.has_perm(self.required_permission):
- return
- raise PermissionDenied
-
- def prepare(self, new_data):
- """
- Makes any necessary preparations to new_data, in place, before data has
- been validated.
- """
- for field in self.fields:
- field.prepare(new_data)
-
- def get_validation_errors(self, new_data):
- "Returns dictionary mapping field_names to error-message lists"
- errors = {}
- self.prepare(new_data)
- for field in self.fields:
- errors.update(field.get_validation_errors(new_data))
- val_name = 'validate_%s' % field.field_name
- if hasattr(self, val_name):
- val = getattr(self, val_name)
- try:
- field.run_validator(new_data, val)
- except (validators.ValidationError, validators.CriticalValidationError), e:
- errors.setdefault(field.field_name, []).extend(e.messages)
-
-# if field.is_required and not new_data.get(field.field_name, False):
-# errors.setdefault(field.field_name, []).append(ugettext_lazy('This field is required.'))
-# continue
-# try:
-# validator_list = field.validator_list
-# if hasattr(self, 'validate_%s' % field.field_name):
-# validator_list.append(getattr(self, 'validate_%s' % field.field_name))
-# for validator in validator_list:
-# if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'):
-# try:
-# if hasattr(field, 'requires_data_list'):
-# validator(new_data.getlist(field.field_name), new_data)
-# else:
-# validator(new_data.get(field.field_name, ''), new_data)
-# except validators.ValidationError, e:
-# errors.setdefault(field.field_name, []).extend(e.messages)
-# # If a CriticalValidationError is raised, ignore any other ValidationErrors
-# # for this particular field
-# except validators.CriticalValidationError, e:
-# errors.setdefault(field.field_name, []).extend(e.messages)
- return errors
-
- def save(self, new_data):
- "Saves the changes and returns the new object"
- # changes is a dictionary-like object keyed by field_name
- raise NotImplementedError
-
- def do_html2python(self, new_data):
- """
- Convert the data from HTML data types to Python datatypes, changing the
- object in place. This happens after validation but before storage. This
- must happen after validation because html2python functions aren't
- expected to deal with invalid input.
- """
- for field in self.fields:
- field.convert_post_data(new_data)
-
-class FormWrapper(object):
- """
- A wrapper linking a Manipulator to the template system.
- This allows dictionary-style lookups of formfields. It also handles feeding
- prepopulated data and validation error messages to the formfield objects.
- """
- def __init__(self, manipulator, data=None, error_dict=None, edit_inline=True):
- self.manipulator = manipulator
- if data is None:
- data = {}
- if error_dict is None:
- error_dict = {}
- self.data = data
- self.error_dict = error_dict
- self._inline_collections = None
- self.edit_inline = edit_inline
-
- def __repr__(self):
- return repr(self.__dict__)
-
- def __getitem__(self, key):
- for field in self.manipulator.fields:
- if field.field_name == key:
- data = field.extract_data(self.data)
- return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, []))
- if self.edit_inline:
- self.fill_inline_collections()
- for inline_collection in self._inline_collections:
- # The 'orig_name' comparison is for backwards compatibility
- # with hand-crafted forms.
- if inline_collection.name == key or (':' not in key and inline_collection.orig_name == key):
- return inline_collection
- raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key
-
- def fill_inline_collections(self):
- if not self._inline_collections:
- ic = []
- related_objects = self.manipulator.get_related_objects()
- for rel_obj in related_objects:
- data = rel_obj.extract_data(self.data)
- inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict)
- ic.append(inline_collection)
- self._inline_collections = ic
-
- def has_errors(self):
- return self.error_dict != {}
-
- def _get_fields(self):
- try:
- return self._fields
- except AttributeError:
- self._fields = [self.__getitem__(field.field_name) for field in self.manipulator.fields]
- return self._fields
-
- fields = property(_get_fields)
-
-class FormFieldWrapper(object):
- "A bridge between the template system and an individual form field. Used by FormWrapper."
- def __init__(self, formfield, data, error_list):
- self.formfield, self.data, self.error_list = formfield, data, error_list
- self.field_name = self.formfield.field_name # for convenience in templates
-
- def __str__(self):
- "Renders the field"
- return unicode(self).encode('utf-8')
-
- def __unicode__(self):
- "Renders the field"
- return force_unicode(self.formfield.render(self.data))
-
- def __repr__(self):
- return '<FormFieldWrapper for "%s">' % self.formfield.field_name
-
- def field_list(self):
- """
- Like __str__(), but returns a list. Use this when the field's render()
- method returns a list.
- """
- return self.formfield.render(self.data)
-
- def errors(self):
- return self.error_list
-
- def html_error_list(self):
- if self.errors():
- return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]))
- else:
- return mark_safe('')
-
- def get_id(self):
- return self.formfield.get_id()
-
-class FormFieldCollection(FormFieldWrapper):
- "A utility class that gives the template access to a dict of FormFieldWrappers"
- def __init__(self, formfield_dict):
- self.formfield_dict = formfield_dict
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- def __unicode__(self):
- return unicode(self.formfield_dict)
-
- def __getitem__(self, template_key):
- "Look up field by template key; raise KeyError on failure"
- return self.formfield_dict[template_key]
-
- def __repr__(self):
- return "<FormFieldCollection: %s>" % self.formfield_dict
-
- def errors(self):
- "Returns list of all errors in this collection's formfields"
- errors = []
- for field in self.formfield_dict.values():
- if hasattr(field, 'errors'):
- errors.extend(field.errors())
- return errors
-
- def has_errors(self):
- return bool(len(self.errors()))
-
- def html_combined_error_list(self):
- return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]))
-
-class InlineObjectCollection(object):
- "An object that acts like a sparse list of form field collections."
- def __init__(self, parent_manipulator, rel_obj, data, errors):
- self.parent_manipulator = parent_manipulator
- self.rel_obj = rel_obj
- self.data = data
- self.errors = errors
- self._collections = None
- self.name = rel_obj.name
- # This is the name used prior to fixing #1839. Needs for backwards
- # compatibility.
- self.orig_name = rel_obj.opts.module_name
-
- def __len__(self):
- self.fill()
- return self._collections.__len__()
-
- def __getitem__(self, k):
- self.fill()
- return self._collections.__getitem__(k)
-
- def __setitem__(self, k, v):
- self.fill()
- return self._collections.__setitem__(k,v)
-
- def __delitem__(self, k):
- self.fill()
- return self._collections.__delitem__(k)
-
- def __iter__(self):
- self.fill()
- return iter(self._collections.values())
-
- def items(self):
- self.fill()
- return self._collections.items()
-
- def fill(self):
- if self._collections:
- return
- else:
- var_name = self.rel_obj.opts.object_name.lower()
- collections = {}
- orig = None
- if hasattr(self.parent_manipulator, 'original_object'):
- orig = self.parent_manipulator.original_object
- orig_list = self.rel_obj.get_list(orig)
-
- for i, instance in enumerate(orig_list):
- collection = {'original': instance}
- for f in self.rel_obj.editable_fields():
- for field_name in f.get_manipulator_field_names(''):
- full_field_name = '%s.%d.%s' % (var_name, i, field_name)
- field = self.parent_manipulator[full_field_name]
- data = field.extract_data(self.data)
- errors = self.errors.get(full_field_name, [])
- collection[field_name] = FormFieldWrapper(field, data, errors)
- collections[i] = FormFieldCollection(collection)
- self._collections = collections
-
-
-class FormField(object):
- """Abstract class representing a form field.
-
- Classes that extend FormField should define the following attributes:
- field_name
- The field's name for use by programs.
- validator_list
- A list of validation tests (callback functions) that the data for
- this field must pass in order to be added or changed.
- is_required
- A Boolean. Is it a required field?
- Subclasses should also implement a render(data) method, which is responsible
- for rending the form field in XHTML.
- """
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- def __unicode__(self):
- return self.render(u'')
-
- def __repr__(self):
- return 'FormField "%s"' % self.field_name
-
- def prepare(self, new_data):
- "Hook for doing something to new_data (in place) before validation."
- pass
-
- def html2python(data):
- "Hook for converting an HTML datatype (e.g. 'on' for checkboxes) to a Python type"
- return data
- html2python = staticmethod(html2python)
-
- def render(self, data):
- raise NotImplementedError
-
- def get_member_name(self):
- if hasattr(self, 'member_name'):
- return self.member_name
- else:
- return self.field_name
-
- def extract_data(self, data_dict):
- if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'):
- data = data_dict.getlist(self.get_member_name())
- else:
- data = data_dict.get(self.get_member_name(), None)
- if data is None:
- data = ''
- return data
-
- def convert_post_data(self, new_data):
- name = self.get_member_name()
- if self.field_name in new_data:
- d = new_data.getlist(self.field_name)
- try:
- converted_data = [self.__class__.html2python(data) for data in d]
- except ValueError:
- converted_data = d
- new_data.setlist(name, converted_data)
- else:
- try:
- #individual fields deal with None values themselves
- new_data.setlist(name, [self.__class__.html2python(None)])
- except EmptyValue:
- new_data.setlist(name, [])
-
-
- def run_validator(self, new_data, validator):
- if self.is_required or new_data.get(self.field_name, False) or hasattr(validator, 'always_test'):
- if hasattr(self, 'requires_data_list'):
- validator(new_data.getlist(self.field_name), new_data)
- else:
- validator(new_data.get(self.field_name, ''), new_data)
-
- def get_validation_errors(self, new_data):
- errors = {}
- if self.is_required and not new_data.get(self.field_name, False):
- errors.setdefault(self.field_name, []).append(ugettext('This field is required.'))
- return errors
- try:
- for validator in self.validator_list:
- try:
- self.run_validator(new_data, validator)
- except validators.ValidationError, e:
- errors.setdefault(self.field_name, []).extend(e.messages)
- # If a CriticalValidationError is raised, ignore any other ValidationErrors
- # for this particular field
- except validators.CriticalValidationError, e:
- errors.setdefault(self.field_name, []).extend(e.messages)
- return errors
-
- def get_id(self):
- "Returns the HTML 'id' attribute for this form field."
- return FORM_FIELD_ID_PREFIX + self.field_name
-
-####################
-# GENERIC WIDGETS #
-####################
-
-class TextField(FormField):
- input_type = "text"
- def __init__(self, field_name, length=30, max_length=None, is_required=False, validator_list=None, member_name=None):
- if validator_list is None: validator_list = []
- self.field_name = field_name
- self.length, self.max_length = length, max_length
- self.is_required = is_required
- self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list
- if member_name != None:
- self.member_name = member_name
-
- def isValidLength(self, data, form):
- if data and self.max_length and len(smart_unicode(data)) > self.max_length:
- raise validators.ValidationError, ungettext("Ensure your text is less than %s character.",
- "Ensure your text is less than %s characters.", self.max_length) % self.max_length
-
- def hasNoNewlines(self, data, form):
- if data and '\n' in data:
- raise validators.ValidationError, ugettext("Line breaks are not allowed here.")
-
- def render(self, data):
- if data is None:
- data = u''
- max_length = u''
- if self.max_length:
- max_length = u'maxlength="%s" ' % self.max_length
- return mark_safe(u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
- (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and u' required' or '',
- self.field_name, self.length, escape(data), max_length))
-
- def html2python(data):
- return data
- html2python = staticmethod(html2python)
-
-class PasswordField(TextField):
- input_type = "password"
-
-class LargeTextField(TextField):
- def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, max_length=None):
- if validator_list is None: validator_list = []
- self.field_name = field_name
- self.rows, self.cols, self.is_required = rows, cols, is_required
- self.validator_list = validator_list[:]
- if max_length:
- self.validator_list.append(self.isValidLength)
- self.max_length = max_length
-
- def render(self, data):
- if data is None:
- data = ''
- return mark_safe(u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
- (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'',
- self.field_name, self.rows, self.cols, escape(data)))
-
-class HiddenField(FormField):
- def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
- if validator_list is None: validator_list = []
- self.field_name, self.is_required = field_name, is_required
- self.validator_list = validator_list[:]
-
- def render(self, data):
- return mark_safe(u'<input type="hidden" id="%s" name="%s" value="%s" />' % \
- (self.get_id(), self.field_name, escape(data)))
-
-class CheckboxField(FormField):
- def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
- if validator_list is None: validator_list = []
- self.field_name = field_name
- self.checked_by_default = checked_by_default
- self.is_required = is_required
- self.validator_list = validator_list[:]
-
- def render(self, data):
- checked_html = ''
- if data or (data is '' and self.checked_by_default):
- checked_html = ' checked="checked"'
- return mark_safe(u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
- (self.get_id(), self.__class__.__name__,
- self.field_name, checked_html))
-
- def html2python(data):
- "Convert value from browser ('on' or '') to a Python boolean"
- if data == 'on':
- return True
- return False
- html2python = staticmethod(html2python)
-
-class SelectField(FormField):
- def __init__(self, field_name, choices=None, size=1, is_required=False, validator_list=None, member_name=None):
- if validator_list is None: validator_list = []
- if choices is None: choices = []
- choices = [(k, smart_unicode(v, strings_only=True)) for k, v in choices]
- self.field_name = field_name
- # choices is a list of (value, human-readable key) tuples because order matters
- self.choices, self.size, self.is_required = choices, size, is_required
- self.validator_list = [self.isValidChoice] + validator_list
- if member_name != None:
- self.member_name = member_name
-
- def render(self, data):
- output = [u'<select id="%s" class="v%s%s" name="%s" size="%s">' % \
- (self.get_id(), self.__class__.__name__,
- self.is_required and u' required' or u'', self.field_name, self.size)]
- str_data = smart_unicode(data) # normalize to string
- for value, display_name in self.choices:
- selected_html = u''
- if smart_unicode(value) == str_data:
- selected_html = u' selected="selected"'
- output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(display_name))))
- output.append(u' </select>')
- return mark_safe(u'\n'.join(output))
-
- def isValidChoice(self, data, form):
- str_data = smart_unicode(data)
- str_choices = [smart_unicode(item[0]) for item in self.choices]
- if str_data not in str_choices:
- raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data': str_data, 'choices': str_choices}
-
-class NullSelectField(SelectField):
- "This SelectField converts blank fields to None"
- def html2python(data):
- if not data:
- return None
- return data
- html2python = staticmethod(html2python)
-
-class RadioSelectField(FormField):
- def __init__(self, field_name, choices=None, ul_class='', is_required=False, validator_list=None, member_name=None):
- if validator_list is None: validator_list = []
- if choices is None: choices = []
- choices = [(k, smart_unicode(v)) for k, v in choices]
- self.field_name = field_name
- # choices is a list of (value, human-readable key) tuples because order matters
- self.choices, self.is_required = choices, is_required
- self.validator_list = [self.isValidChoice] + validator_list
- self.ul_class = ul_class
- if member_name != None:
- self.member_name = member_name
-
- def render(self, data):
- """
- Returns a special object, RadioFieldRenderer, that is iterable *and*
- has a default unicode() rendered output.
-
- This allows for flexible use in templates. You can just use the default
- rendering:
-
- {{ field_name }}
-
- ...which will output the radio buttons in an unordered list.
- Or, you can manually traverse each radio option for special layout:
-
- {% for option in field_name.field_list %}
- {{ option.field }} {{ option.label }}<br />
- {% endfor %}
- """
- class RadioFieldRenderer:
- def __init__(self, datalist, ul_class):
- self.datalist, self.ul_class = datalist, ul_class
- def __unicode__(self):
- "Default unicode() output for this radio field -- a <ul>"
- output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')]
- output.extend([u'<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
- output.append(u'</ul>')
- return mark_safe(u''.join(output))
- def __iter__(self):
- for d in self.datalist:
- yield d
- def __len__(self):
- return len(self.datalist)
- datalist = []
- str_data = smart_unicode(data) # normalize to string
- for i, (value, display_name) in enumerate(self.choices):
- selected_html = ''
- if smart_unicode(value) == str_data:
- selected_html = u' checked="checked"'
- datalist.append({
- 'value': value,
- 'name': display_name,
- 'field': mark_safe(u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
- (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html)),
- 'label': mark_safe(u'<label for="%s">%s</label>' % \
- (self.get_id() + u'_' + unicode(i), display_name),
- )})
- return RadioFieldRenderer(datalist, self.ul_class)
-
- def isValidChoice(self, data, form):
- str_data = smart_unicode(data)
- str_choices = [smart_unicode(item[0]) for item in self.choices]
- if str_data not in str_choices:
- raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':str_data, 'choices':str_choices}
-
-class NullBooleanField(SelectField):
- "This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- SelectField.__init__(self, field_name, choices=[('1', ugettext('Unknown')), ('2', ugettext('Yes')), ('3', ugettext('No'))],
- is_required=is_required, validator_list=validator_list)
-
- def render(self, data):
- if data is None: data = '1'
- elif data == True: data = '2'
- elif data == False: data = '3'
- return SelectField.render(self, data)
-
- def html2python(data):
- return {None: None, '1': None, '2': True, '3': False}[data]
- html2python = staticmethod(html2python)
-
-class SelectMultipleField(SelectField):
- requires_data_list = True
- def render(self, data):
- output = [u'<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \
- (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'',
- self.field_name, self.size)]
- str_data_list = map(smart_unicode, data) # normalize to strings
- for value, choice in self.choices:
- selected_html = u''
- if smart_unicode(value) in str_data_list:
- selected_html = u' selected="selected"'
- output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(choice))))
- output.append(u' </select>')
- return mark_safe(u'\n'.join(output))
-
- def isValidChoice(self, field_data, all_data):
- # data is something like ['1', '2', '3']
- str_choices = [smart_unicode(item[0]) for item in self.choices]
- for val in map(smart_unicode, field_data):
- if val not in str_choices:
- raise validators.ValidationError, ugettext("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':val, 'choices':str_choices}
-
- def html2python(data):
- if data is None:
- raise EmptyValue
- return data
- html2python = staticmethod(html2python)
-
-class CheckboxSelectMultipleField(SelectMultipleField):
- """
- This has an identical interface to SelectMultipleField, except the rendered
- widget is different. Instead of a <select multiple>, this widget outputs a
- <ul> of <input type="checkbox">es.
-
- Of course, that results in multiple form elements for the same "single"
- field, so this class's prepare() method flattens the split data elements
- back into the single list that validators, renderers and save() expect.
- """
- requires_data_list = True
- def __init__(self, field_name, choices=None, ul_class='', validator_list=None):
- if validator_list is None: validator_list = []
- if choices is None: choices = []
- self.ul_class = ul_class
- SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
-
- def prepare(self, new_data):
- # new_data has "split" this field into several fields, so flatten it
- # back into a single list.
- data_list = []
- for value, readable_value in self.choices:
- if new_data.get('%s%s' % (self.field_name, value), '') == 'on':
- data_list.append(value)
- new_data.setlist(self.field_name, data_list)
-
- def render(self, data):
- output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')]
- str_data_list = map(smart_unicode, data) # normalize to strings
- for value, choice in self.choices:
- checked_html = u''
- if smart_unicode(value) in str_data_list:
- checked_html = u' checked="checked"'
- field_name = u'%s%s' % (self.field_name, value)
- output.append(u'<li><input type="checkbox" id="%s" class="v%s" name="%s"%s value="on" /> <label for="%s">%s</label></li>' % \
- (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
- self.get_id() + escape(value), choice))
- output.append(u'</ul>')
- return mark_safe(u'\n'.join(output))
-
-####################
-# FILE UPLOADS #
-####################
-
-class FileUploadField(FormField):
- def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
- if validator_list is None: validator_list = []
- self.field_name, self.is_required = field_name, is_required
- self.validator_list = [self.isNonEmptyFile] + validator_list
-
- def isNonEmptyFile(self, new_data, all_data):
- if hasattr(new_data, 'upload_errors'):
- upload_errors = new_data.upload_errors()
- if upload_errors:
- raise validators.CriticalValidationError, upload_errors
- try:
- file_size = new_data.size
- except AttributeError:
- file_size = len(new_data['content'])
- if not file_size:
- raise validators.CriticalValidationError, ugettext("The submitted file is empty.")
-
- def render(self, data):
- return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \
- (self.get_id(), self.__class__.__name__, self.field_name))
-
- def prepare(self, new_data):
- if hasattr(new_data, 'upload_errors'):
- upload_errors = new_data.upload_errors()
- new_data[self.field_name] = { '_file_upload_error': upload_errors }
-
- def html2python(data):
- if data is None:
- raise EmptyValue
- return data
- html2python = staticmethod(html2python)
-
-class ImageUploadField(FileUploadField):
- "A FileUploadField that raises CriticalValidationError if the uploaded file isn't an image."
- def __init__(self, *args, **kwargs):
- FileUploadField.__init__(self, *args, **kwargs)
- self.validator_list.insert(0, self.isValidImage)
-
- def isValidImage(self, field_data, all_data):
- try:
- validators.isValidImage(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
-####################
-# INTEGERS/FLOATS #
-####################
-
-class IntegerField(TextField):
- def __init__(self, field_name, length=10, max_length=None, is_required=False, validator_list=None, member_name=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isInteger] + validator_list
- if member_name is not None:
- self.member_name = member_name
- TextField.__init__(self, field_name, length, max_length, is_required, validator_list)
-
- def isInteger(self, field_data, all_data):
- try:
- validators.isInteger(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- if data == '' or data is None:
- return None
- return int(data)
- html2python = staticmethod(html2python)
-
-class SmallIntegerField(IntegerField):
- def __init__(self, field_name, length=5, max_length=5, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isSmallInteger] + validator_list
- IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
-
- def isSmallInteger(self, field_data, all_data):
- if not -32768 <= int(field_data) <= 32767:
- raise validators.CriticalValidationError, ugettext("Enter a whole number between -32,768 and 32,767.")
-
-class PositiveIntegerField(IntegerField):
- def __init__(self, field_name, length=10, max_length=None, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isPositive] + validator_list
- IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
-
- def isPositive(self, field_data, all_data):
- if int(field_data) < 0:
- raise validators.CriticalValidationError, ugettext("Enter a positive number.")
-
-class PositiveSmallIntegerField(IntegerField):
- def __init__(self, field_name, length=5, max_length=None, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isPositiveSmall] + validator_list
- IntegerField.__init__(self, field_name, length, max_length, is_required, validator_list)
-
- def isPositiveSmall(self, field_data, all_data):
- if not 0 <= int(field_data) <= 32767:
- raise validators.CriticalValidationError, ugettext("Enter a whole number between 0 and 32,767.")
-
-class FloatField(TextField):
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [validators.isValidFloat] + validator_list
- TextField.__init__(self, field_name, is_required=is_required, validator_list=validator_list)
-
- def html2python(data):
- if data == '' or data is None:
- return None
- return float(data)
- html2python = staticmethod(html2python)
-
-class DecimalField(TextField):
- def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- self.max_digits, self.decimal_places = max_digits, decimal_places
- validator_list = [self.isValidDecimal] + validator_list
- # Initialise the TextField, making sure it's large enough to fit the number with a - sign and a decimal point.
- super(DecimalField, self).__init__(field_name, max_digits+2, max_digits+2, is_required, validator_list)
-
- def isValidDecimal(self, field_data, all_data):
- v = validators.IsValidDecimal(self.max_digits, self.decimal_places)
- try:
- v(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- if data == '' or data is None:
- return None
- try:
- import decimal
- except ImportError:
- from django.utils import _decimal as decimal
- try:
- return decimal.Decimal(data)
- except decimal.InvalidOperation, e:
- raise ValueError, e
- html2python = staticmethod(html2python)
-
-####################
-# DATES AND TIMES #
-####################
-
-class DatetimeField(TextField):
- """A FormField that automatically converts its data to a datetime.datetime object.
- The data should be in the format YYYY-MM-DD HH:MM:SS."""
- def __init__(self, field_name, length=30, max_length=None, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- self.field_name = field_name
- self.length, self.max_length = length, max_length
- self.is_required = is_required
- self.validator_list = [validators.isValidANSIDatetime] + validator_list
-
- def html2python(data):
- "Converts the field into a datetime.datetime object"
- import datetime
- try:
- date, time = data.split()
- y, m, d = date.split('-')
- timebits = time.split(':')
- h, mn = timebits[:2]
- if len(timebits) > 2:
- s = int(timebits[2])
- else:
- s = 0
- return datetime.datetime(int(y), int(m), int(d), int(h), int(mn), s)
- except ValueError:
- return None
- html2python = staticmethod(html2python)
-
-class DateField(TextField):
- """A FormField that automatically converts its data to a datetime.date object.
- The data should be in the format YYYY-MM-DD."""
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidDate] + validator_list
- TextField.__init__(self, field_name, length=10, max_length=10,
- is_required=is_required, validator_list=validator_list)
-
- def isValidDate(self, field_data, all_data):
- try:
- validators.isValidANSIDate(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- "Converts the field into a datetime.date object"
- import time, datetime
- try:
- time_tuple = time.strptime(data, '%Y-%m-%d')
- return datetime.date(*time_tuple[0:3])
- except (ValueError, TypeError):
- return None
- html2python = staticmethod(html2python)
-
-class TimeField(TextField):
- """A FormField that automatically converts its data to a datetime.time object.
- The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidTime] + validator_list
- TextField.__init__(self, field_name, length=8, max_length=8,
- is_required=is_required, validator_list=validator_list)
-
- def isValidTime(self, field_data, all_data):
- try:
- validators.isValidANSITime(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- "Converts the field into a datetime.time object"
- import time, datetime
- try:
- part_list = data.split('.')
- try:
- time_tuple = time.strptime(part_list[0], '%H:%M:%S')
- except ValueError: # seconds weren't provided
- time_tuple = time.strptime(part_list[0], '%H:%M')
- t = datetime.time(*time_tuple[3:6])
- if (len(part_list) == 2):
- t = t.replace(microsecond=int(part_list[1]))
- return t
- except (ValueError, TypeError, AttributeError):
- return None
- html2python = staticmethod(html2python)
-
-####################
-# INTERNET-RELATED #
-####################
-
-class EmailField(TextField):
- "A convenience FormField for validating e-mail addresses"
- def __init__(self, field_name, length=50, max_length=75, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidEmail] + validator_list
- TextField.__init__(self, field_name, length, max_length=max_length,
- is_required=is_required, validator_list=validator_list)
-
- def isValidEmail(self, field_data, all_data):
- try:
- validators.isValidEmail(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
-class URLField(TextField):
- "A convenience FormField for validating URLs"
- def __init__(self, field_name, length=50, max_length=200, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidURL] + validator_list
- TextField.__init__(self, field_name, length=length, max_length=max_length,
- is_required=is_required, validator_list=validator_list)
-
- def isValidURL(self, field_data, all_data):
- try:
- validators.isValidURL(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
-class IPAddressField(TextField):
- def __init__(self, field_name, length=15, max_length=15, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidIPAddress] + validator_list
- TextField.__init__(self, field_name, length=length, max_length=max_length,
- is_required=is_required, validator_list=validator_list)
-
- def isValidIPAddress(self, field_data, all_data):
- try:
- validators.isValidIPAddress4(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- return data or None
- html2python = staticmethod(html2python)
-
-####################
-# MISCELLANEOUS #
-####################
-
-class FilePathField(SelectField):
- "A SelectField whose choices are the files in a given directory."
- def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None, max_length=None):
- import os
- from django.db.models import BLANK_CHOICE_DASH
- if match is not None:
- import re
- match_re = re.compile(match)
- choices = not is_required and BLANK_CHOICE_DASH[:] or []
- if recursive:
- for root, dirs, files in os.walk(path):
- for f in files:
- if match is None or match_re.search(f):
- f = os.path.join(root, f)
- choices.append((f, f.replace(path, "", 1)))
- else:
- try:
- for f in os.listdir(path):
- full_file = os.path.join(path, f)
- if os.path.isfile(full_file) and (match is None or match_re.search(f)):
- choices.append((full_file, f))
- except OSError:
- pass
- SelectField.__init__(self, field_name, choices, 1, is_required, validator_list)
-
-class PhoneNumberField(TextField):
- "A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidPhone] + validator_list
- TextField.__init__(self, field_name, length=12, max_length=12,
- is_required=is_required, validator_list=validator_list)
-
- def isValidPhone(self, field_data, all_data):
- try:
- validators.isValidPhone(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
-class USStateField(TextField):
- "A convenience FormField for validating U.S. states (e.g. 'IL')"
- def __init__(self, field_name, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isValidUSState] + validator_list
- TextField.__init__(self, field_name, length=2, max_length=2,
- is_required=is_required, validator_list=validator_list)
-
- def isValidUSState(self, field_data, all_data):
- try:
- validators.isValidUSState(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def html2python(data):
- if data:
- return data.upper() # Should always be stored in upper case
- return data
- html2python = staticmethod(html2python)
-
-class CommaSeparatedIntegerField(TextField):
- "A convenience FormField for validating comma-separated integer fields"
- def __init__(self, field_name, max_length=None, is_required=False, validator_list=None):
- if validator_list is None: validator_list = []
- validator_list = [self.isCommaSeparatedIntegerList] + validator_list
- TextField.__init__(self, field_name, length=20, max_length=max_length,
- is_required=is_required, validator_list=validator_list)
-
- def isCommaSeparatedIntegerList(self, field_data, all_data):
- try:
- validators.isCommaSeparatedIntegerList(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
-
- def render(self, data):
- if data is None:
- data = u''
- elif isinstance(data, (list, tuple)):
- data = u','.join(data)
- return super(CommaSeparatedIntegerField, self).render(data)
-
-class RawIdAdminField(CommaSeparatedIntegerField):
- def html2python(data):
- if data:
- return data.split(',')
- else:
- return []
- html2python = staticmethod(html2python)
-
-class XMLLargeTextField(LargeTextField):
- """
- A LargeTextField with an XML validator. The schema_path argument is the
- full path to a Relax NG compact schema to validate against.
- """
- def __init__(self, field_name, schema_path, **kwargs):
- self.schema_path = schema_path
- kwargs.setdefault('validator_list', []).insert(0, self.isValidXML)
- LargeTextField.__init__(self, field_name, **kwargs)
-
- def isValidXML(self, field_data, all_data):
- v = validators.RelaxNGCompact(self.schema_path)
- try:
- v(field_data, all_data)
- except validators.ValidationError, e:
- raise validators.CriticalValidationError, e.messages
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index d1b7dddc8b..6b164803d0 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -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
--------------------
diff --git a/docs/intro/whatsnext.txt b/docs/intro/whatsnext.txt
index ae67220f58..c3cc962341 100644
--- a/docs/intro/whatsnext.txt
+++ b/docs/intro/whatsnext.txt
@@ -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
----------------
diff --git a/docs/obsolete/forms.txt b/docs/obsolete/forms.txt
deleted file mode 100644
index 9bfd4a979e..0000000000
--- a/docs/obsolete/forms.txt
+++ /dev/null
@@ -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).
diff --git a/docs/obsolete/newforms-migration.txt b/docs/obsolete/newforms-migration.txt
deleted file mode 100644
index da56b5704e..0000000000
--- a/docs/obsolete/newforms-migration.txt
+++ /dev/null
@@ -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.
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index 8ea4b2385c..7654300617 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -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
--------------------
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 7faa07d4f7..909bfeff1f 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -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.
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index f93bd0fbf5..d161b3f7f5 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -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``
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
index c8c06d91c9..42fa1fb9c3 100644
--- a/docs/topics/testing.txt
+++ b/docs/topics/testing.txt
@@ -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
diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py
index baf07a072f..c776146de5 100644
--- a/tests/modeltests/field_subclassing/models.py
+++ b/tests/modeltests/field_subclassing/models.py
@@ -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')
diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py
index abd72b1293..28172089ab 100644
--- a/tests/modeltests/invalid_models/models.py
+++ b/tests/modeltests/invalid_models/models.py
@@ -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")
diff --git a/tests/modeltests/manipulators/__init__.py b/tests/modeltests/manipulators/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/tests/modeltests/manipulators/__init__.py
+++ /dev/null
diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py
deleted file mode 100644
index 0adee9ed39..0000000000
--- a/tests/modeltests/manipulators/models.py
+++ /dev/null
@@ -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
-"""}
diff --git a/tests/modeltests/mutually_referential/models.py b/tests/modeltests/mutually_referential/models.py
index 5176721f3d..2cbaa4b50b 100644
--- a/tests/modeltests/mutually_referential/models.py
+++ b/tests/modeltests/mutually_referential/models.py
@@ -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")
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
index 5aedcd15fc..0e67c60ac9 100644
--- a/tests/regressiontests/model_fields/tests.py
+++ b/tests/regressiontests/model_fields/tests.py
@@ -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)