Skip to content
Browse files

schema-evolution: update from HEAD (v5821)

git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5822 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 0af6ed0 commit 5aa017255827b2c06bd9a5f7f069828ef625da18 @keredson keredson committed Aug 6, 2007
Showing with 1,067 additions and 486 deletions.
  1. +2 −0 AUTHORS
  2. +1 −1 django/contrib/admin/models.py
  3. +1 −1 django/contrib/admin/templatetags/admin_modify.py
  4. +2 −2 django/contrib/admin/views/doc.py
  5. +1 −1 django/contrib/admin/views/main.py
  6. +10 −10 django/contrib/auth/forms.py
  7. +7 −7 django/contrib/auth/models.py
  8. +4 −4 django/contrib/comments/models.py
  9. +3 −3 django/contrib/comments/views/comments.py
  10. +3 −3 django/contrib/contenttypes/models.py
  11. +3 −3 django/contrib/flatpages/models.py
  12. +2 −2 django/contrib/redirects/models.py
  13. +1 −1 django/contrib/sessions/models.py
  14. +2 −2 django/contrib/sites/models.py
  15. +15 −10 django/core/management.py
  16. +1 −1 django/core/validators.py
  17. +3 −3 django/db/backends/ado_mssql/creation.py
  18. +3 −3 django/db/backends/mysql/creation.py
  19. +3 −3 django/db/backends/mysql_old/creation.py
  20. +2 −2 django/db/backends/oracle/creation.py
  21. +3 −3 django/db/backends/postgresql/creation.py
  22. +3 −3 django/db/backends/sqlite3/creation.py
  23. +1 −1 django/db/backends/sqlite3/introspection.py
  24. +40 −9 django/db/models/fields/__init__.py
  25. +3 −0 django/db/models/fields/related.py
  26. +1 −1 django/newforms/extras/widgets.py
  27. +53 −3 django/newforms/fields.py
  28. +7 −6 django/newforms/forms.py
  29. +14 −8 django/newforms/models.py
  30. +13 −6 django/newforms/widgets.py
  31. +60 −56 django/oldforms/__init__.py
  32. +22 −9 django/template/defaulttags.py
  33. +0 −1 django/utils/encoding.py
  34. +67 −0 django/utils/maxlength.py
  35. +1 −1 docs/add_ons.txt
  36. +1 −4 docs/api_stability.txt
  37. +1 −1 docs/authentication.txt
  38. +10 −10 docs/contributing.txt
  39. +1 −1 docs/databases.txt
  40. +7 −7 docs/db-api.txt
  41. +1 −1 docs/email.txt
  42. +3 −0 docs/faq.txt
  43. +5 −5 docs/forms.txt
  44. +71 −59 docs/model-api.txt
  45. +140 −11 docs/newforms.txt
  46. +7 −5 docs/overview.txt
  47. +1 −1 docs/release_notes_0.95.txt
  48. +1 −1 docs/request_response.txt
  49. +1 −1 docs/sitemaps.txt
  50. +5 −5 docs/sites.txt
  51. +2 −2 docs/testing.txt
  52. +3 −3 docs/tutorial01.txt
  53. +1 −1 docs/tutorial02.txt
  54. +2 −1 docs/tutorial03.txt
  55. +3 −3 docs/tutorial04.txt
  56. +1 −1 docs/url_dispatch.txt
  57. +1 −1 tests/modeltests/basic/models.py
  58. +2 −2 tests/modeltests/choices/models.py
  59. +3 −3 tests/modeltests/custom_columns/models.py
  60. +5 −5 tests/modeltests/custom_managers/models.py
  61. +1 −1 tests/modeltests/custom_methods/models.py
  62. +4 −4 tests/modeltests/custom_pk/models.py
  63. +1 −1 tests/modeltests/field_defaults/models.py
  64. +1 −1 tests/modeltests/fixtures/models.py
  65. +4 −4 tests/modeltests/generic_relations/models.py
  66. +2 −2 tests/modeltests/get_latest/models.py
  67. +2 −2 tests/modeltests/get_object_or_404/models.py
  68. +2 −2 tests/modeltests/get_or_create/models.py
  69. +22 −22 tests/modeltests/invalid_models/models.py
  70. +1 −1 tests/modeltests/lookup/models.py
  71. +1 −1 tests/modeltests/m2m_and_m2o/models.py
  72. +4 −4 tests/modeltests/m2m_intermediary/models.py
  73. +2 −2 tests/modeltests/m2m_multiple/models.py
  74. +1 −1 tests/modeltests/m2m_recursive/models.py
  75. +1 −1 tests/modeltests/m2o_recursive/models.py
  76. +1 −1 tests/modeltests/m2o_recursive2/models.py
  77. +3 −3 tests/modeltests/manipulators/models.py
  78. +2 −2 tests/modeltests/many_to_many/models.py
  79. +3 −3 tests/modeltests/many_to_one/models.py
  80. +2 −2 tests/modeltests/many_to_one_null/models.py
  81. +27 −5 tests/modeltests/model_forms/models.py
  82. +2 −2 tests/modeltests/model_inheritance/models.py
  83. +2 −2 tests/modeltests/mutually_referential/models.py
  84. +6 −6 tests/modeltests/one_to_one/models.py
  85. +1 −1 tests/modeltests/or_lookups/models.py
  86. +1 −1 tests/modeltests/ordering/models.py
  87. +1 −1 tests/modeltests/pagination/models.py
  88. +2 −2 tests/modeltests/properties/models.py
  89. +8 −8 tests/modeltests/reserved_names/models.py
  90. +3 −3 tests/modeltests/reverse_lookup/models.py
  91. +2 −2 tests/modeltests/save_delete_hooks/models.py
  92. +8 −8 tests/modeltests/select_related/models.py
  93. +3 −3 tests/modeltests/serializers/models.py
  94. +2 −2 tests/modeltests/str/models.py
  95. +2 −2 tests/modeltests/transactions/models.py
  96. +1 −1 tests/modeltests/validation/models.py
  97. +1 −1 tests/regressiontests/bug639/models.py
  98. +1 −1 tests/regressiontests/datatypes/models.py
  99. +4 −4 tests/regressiontests/fixtures_regress/models.py
  100. +74 −7 tests/regressiontests/forms/tests.py
  101. +1 −1 tests/regressiontests/initial_sql_regress/models.py
  102. +37 −37 tests/regressiontests/invalid_admin_options/models.py
  103. +3 −3 tests/regressiontests/many_to_one_regress/models.py
  104. 0 tests/regressiontests/maxlength/__init__.py
  105. 0 tests/regressiontests/maxlength/models.py
  106. +160 −0 tests/regressiontests/maxlength/tests.py
  107. +1 −1 tests/regressiontests/model_regress/models.py
  108. +2 −2 tests/regressiontests/null_queries/models.py
  109. +3 −3 tests/regressiontests/one_to_one_regress/models.py
  110. +8 −8 tests/regressiontests/serializers_regress/models.py
  111. +6 −6 tests/regressiontests/string_lookup/models.py
View
2 AUTHORS
@@ -45,6 +45,7 @@ answer newbie questions, and generally made Django that much better:
Marty Alchin <gulopine@gamemusic.org>
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
AgarFu <heaven@croasanaso.sytes.net>
+ Derek Anderson <public@kered.org>
Andreas
andy@jadedplanet.net
Fabrice Aneche <akh@nobugware.com>
@@ -96,6 +97,7 @@ answer newbie questions, and generally made Django that much better:
Maximillian Dornseif <md@hudora.de>
Jeremy Dunck <http://dunck.us/>
Andrew Durdin <adurdin@gmail.com>
+ dusk@woofle.net
Andy Dustman <farcepest@gmail.com>
Clint Ecker
enlight
View
2 django/contrib/admin/models.py
@@ -18,7 +18,7 @@ class LogEntry(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.TextField(_('object id'), blank=True, null=True)
- object_repr = models.CharField(_('object repr'), maxlength=200)
+ object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)
objects = LogEntryManager()
View
2 django/contrib/admin/templatetags/admin_modify.py
@@ -192,7 +192,7 @@ def auto_populated_field_script(auto_pop_fields, change = False):
t.append(u'document.getElementById("id_%s").onkeyup = function() {' \
' var e = document.getElementById("id_%s");' \
' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
- f, field.name, add_values, field.maxlength))
+ f, field.name, add_values, field.max_length))
return u''.join(t)
auto_populated_field_script = register.simple_tag(auto_populated_field_script)
View
4 django/contrib/admin/views/doc.py
@@ -291,7 +291,7 @@ def get_return_data_type(func_name):
DATA_TYPE_MAPPING = {
'AutoField' : _('Integer'),
'BooleanField' : _('Boolean (Either True or False)'),
- 'CharField' : _('String (up to %(maxlength)s)'),
+ 'CharField' : _('String (up to %(max_length)s)'),
'CommaSeparatedIntegerField': _('Comma-separated integers'),
'DateField' : _('Date (without time)'),
'DateTimeField' : _('Date (with time)'),
@@ -310,7 +310,7 @@ def get_return_data_type(func_name):
'PhoneNumberField' : _('Phone number'),
'PositiveIntegerField' : _('Integer'),
'PositiveSmallIntegerField' : _('Integer'),
- 'SlugField' : _('String (up to %(maxlength)s)'),
+ 'SlugField' : _('String (up to %(max_length)s)'),
'SmallIntegerField' : _('Integer'),
'TextField' : _('Text'),
'TimeField' : _('Time'),
View
2 django/contrib/admin/views/main.py
@@ -673,7 +673,7 @@ def get_ordering(self):
try:
attr = getattr(lookup_opts.admin.manager.model, field_name)
order_field = attr.admin_order_field
- except IndexError:
+ except AttributeError:
pass
else:
if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
View
20 django/contrib/auth/forms.py
@@ -10,10 +10,10 @@ class UserCreationForm(oldforms.Manipulator):
"A form that creates a user, with no privileges, from the given username and password."
def __init__(self):
self.fields = (
- oldforms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
+ oldforms.TextField(field_name='username', length=30, max_length=30, is_required=True,
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
- oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
- oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
+ oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
+ oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
@@ -42,9 +42,9 @@ def __init__(self, request=None):
"""
self.request = request
self.fields = [
- oldforms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
+ oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
- oldforms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
+ oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
]
self.user_cache = None
@@ -111,11 +111,11 @@ class PasswordChangeForm(oldforms.Manipulator):
def __init__(self, user):
self.user = user
self.fields = (
- oldforms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
+ oldforms.PasswordField(field_name="old_password", length=30, max_length=30, is_required=True,
validator_list=[self.isValidOldPassword]),
- oldforms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
+ oldforms.PasswordField(field_name="new_password1", length=30, max_length=30, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
- oldforms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
+ oldforms.PasswordField(field_name="new_password2", length=30, max_length=30, is_required=True),
)
def isValidOldPassword(self, new_data, all_data):
@@ -133,8 +133,8 @@ class AdminPasswordChangeForm(oldforms.Manipulator):
def __init__(self, user):
self.user = user
self.fields = (
- oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
- oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
+ oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
+ oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
View
14 django/contrib/auth/models.py
@@ -50,9 +50,9 @@ class Permission(models.Model):
Three basic permissions -- add, change and delete -- are automatically created for each Django model.
"""
- name = models.CharField(_('name'), maxlength=50)
+ name = models.CharField(_('name'), max_length=50)
content_type = models.ForeignKey(ContentType)
- codename = models.CharField(_('codename'), maxlength=100)
+ codename = models.CharField(_('codename'), max_length=100)
class Meta:
verbose_name = _('permission')
@@ -70,7 +70,7 @@ class Group(models.Model):
Beyond permissions, groups are a convenient way to categorize users to apply some label, or extended functionality, to them. For example, you could create a group 'Special users', and you could write code that would do special things to those users -- such as giving them access to a members-only portion of your site, or sending them members-only e-mail messages.
"""
- name = models.CharField(_('name'), maxlength=80, unique=True)
+ name = models.CharField(_('name'), max_length=80, unique=True)
permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True, filter_interface=models.HORIZONTAL)
class Meta:
@@ -108,11 +108,11 @@ class User(models.Model):
Username and password are required. Other fields are optional.
"""
- username = models.CharField(_('username'), maxlength=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
- first_name = models.CharField(_('first name'), maxlength=30, blank=True)
- last_name = models.CharField(_('last name'), maxlength=30, blank=True)
+ username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
+ first_name = models.CharField(_('first name'), max_length=30, blank=True)
+ last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
- password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
+ password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
View
8 django/contrib/comments/models.py
@@ -65,8 +65,8 @@ class Comment(models.Model):
user = models.ForeignKey(User, raw_id_admin=True)
content_type = models.ForeignKey(ContentType)
object_id = models.IntegerField(_('object ID'))
- headline = models.CharField(_('headline'), maxlength=255, blank=True)
- comment = models.TextField(_('comment'), maxlength=3000)
+ headline = models.CharField(_('headline'), max_length=255, blank=True)
+ comment = models.TextField(_('comment'), max_length=3000)
rating1 = models.PositiveSmallIntegerField(_('rating #1'), blank=True, null=True)
rating2 = models.PositiveSmallIntegerField(_('rating #2'), blank=True, null=True)
rating3 = models.PositiveSmallIntegerField(_('rating #3'), blank=True, null=True)
@@ -164,8 +164,8 @@ class FreeComment(models.Model):
# A FreeComment is a comment by a non-registered user.
content_type = models.ForeignKey(ContentType)
object_id = models.IntegerField(_('object ID'))
- comment = models.TextField(_('comment'), maxlength=3000)
- person_name = models.CharField(_("person's name"), maxlength=50)
+ comment = models.TextField(_('comment'), max_length=3000)
+ person_name = models.CharField(_("person's name"), max_length=50)
submit_date = models.DateTimeField(_('date/time submitted'), auto_now_add=True)
is_public = models.BooleanField(_('is public'))
ip_address = models.IPAddressField(_('ip address'))
View
6 django/contrib/comments/views/comments.py
@@ -29,7 +29,7 @@ def get_validator_list(rating_num):
else:
return []
self.fields.extend([
- oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
+ oldforms.LargeTextField(field_name="comment", max_length=3000, is_required=True,
validator_list=[self.hasNoProfanities]),
oldforms.RadioSelectField(field_name="rating1", choices=choices,
is_required=ratings_required and num_rating_choices > 0,
@@ -122,9 +122,9 @@ class PublicFreeCommentManipulator(oldforms.Manipulator):
"Manipulator that handles public free (unregistered) comments"
def __init__(self):
self.fields = (
- oldforms.TextField(field_name="person_name", maxlength=50, is_required=True,
+ oldforms.TextField(field_name="person_name", max_length=50, is_required=True,
validator_list=[self.hasNoProfanities]),
- oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
+ oldforms.LargeTextField(field_name="comment", max_length=3000, is_required=True,
validator_list=[self.hasNoProfanities]),
)
View
6 django/contrib/contenttypes/models.py
@@ -32,9 +32,9 @@ def clear_cache(self):
CONTENT_TYPE_CACHE = {}
class ContentType(models.Model):
- name = models.CharField(maxlength=100)
- app_label = models.CharField(maxlength=100)
- model = models.CharField(_('python model class name'), maxlength=100)
+ name = models.CharField(max_length=100)
+ app_label = models.CharField(max_length=100)
+ model = models.CharField(_('python model class name'), max_length=100)
objects = ContentTypeManager()
class Meta:
verbose_name = _('content type')
View
6 django/contrib/flatpages/models.py
@@ -4,12 +4,12 @@
from django.utils.translation import ugettext_lazy as _
class FlatPage(models.Model):
- url = models.CharField(_('URL'), maxlength=100, validator_list=[validators.isAlphaNumericURL], db_index=True,
+ url = models.CharField(_('URL'), max_length=100, validator_list=[validators.isAlphaNumericURL], db_index=True,
help_text=_("Example: '/about/contact/'. Make sure to have leading and trailing slashes."))
- title = models.CharField(_('title'), maxlength=200)
+ title = models.CharField(_('title'), max_length=200)
content = models.TextField(_('content'))
enable_comments = models.BooleanField(_('enable comments'))
- template_name = models.CharField(_('template name'), maxlength=70, blank=True,
+ template_name = models.CharField(_('template name'), max_length=70, blank=True,
help_text=_("Example: 'flatpages/contact_page.html'. If this isn't provided, the system will use 'flatpages/default.html'."))
registration_required = models.BooleanField(_('registration required'), help_text=_("If this is checked, only logged-in users will be able to view the page."))
sites = models.ManyToManyField(Site)
View
4 django/contrib/redirects/models.py
@@ -4,9 +4,9 @@
class Redirect(models.Model):
site = models.ForeignKey(Site, radio_admin=models.VERTICAL)
- old_path = models.CharField(_('redirect from'), maxlength=200, db_index=True,
+ old_path = models.CharField(_('redirect from'), max_length=200, db_index=True,
help_text=_("This should be an absolute path, excluding the domain name. Example: '/events/search/'."))
- new_path = models.CharField(_('redirect to'), maxlength=200, blank=True,
+ new_path = models.CharField(_('redirect to'), max_length=200, blank=True,
help_text=_("This can be either an absolute path (as above) or a full URL starting with 'http://'."))
class Meta:
View
2 django/contrib/sessions/models.py
@@ -65,7 +65,7 @@ class Session(models.Model):
the sessions documentation that is shipped with Django (also available
on the Django website).
"""
- session_key = models.CharField(_('session key'), maxlength=40, primary_key=True)
+ session_key = models.CharField(_('session key'), max_length=40, primary_key=True)
session_data = models.TextField(_('session data'))
expire_date = models.DateTimeField(_('expire date'))
objects = SessionManager()
View
4 django/contrib/sites/models.py
@@ -12,8 +12,8 @@ def get_current(self):
return self.get(pk=sid)
class Site(models.Model):
- domain = models.CharField(_('domain name'), maxlength=100)
- name = models.CharField(_('display name'), maxlength=50)
+ domain = models.CharField(_('domain name'), max_length=100)
+ name = models.CharField(_('display name'), max_length=50)
objects = SiteManager()
class Meta:
db_table = 'django_site'
View
25 django/core/management.py
@@ -1064,9 +1064,9 @@ def inspectdb():
field_type, new_params = field_type
extra_params.update(new_params)
- # Add maxlength for all CharFields.
+ # Add max_length for all CharFields.
if field_type == 'CharField' and row[3]:
- extra_params['maxlength'] = row[3]
+ extra_params['max_length'] = row[3]
if field_type == 'DecimalField':
extra_params['max_digits'] = row[4]
@@ -1141,8 +1141,8 @@ def get_validation_errors(outfile, app=None):
for f in opts.fields:
if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name)
- if isinstance(f, models.CharField) and f.maxlength in (None, 0):
- e.add(opts, '"%s": CharFields require a "maxlength" attribute.' % f.name)
+ if isinstance(f, models.CharField) and f.max_length in (None, 0):
+ e.add(opts, '"%s": CharFields require a "max_length" attribute.' % f.name)
if isinstance(f, models.DecimalField):
if f.decimal_places is None:
e.add(opts, '"%s": DecimalFields require a "decimal_places" attribute.' % f.name)
@@ -1167,11 +1167,11 @@ def get_validation_errors(outfile, app=None):
if f.db_index not in (None, True, False):
e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
- # Check that maxlength <= 255 if using older MySQL versions.
+ # Check that max_length <= 255 if using older MySQL versions.
if settings.DATABASE_ENGINE == 'mysql':
db_version = connection.get_server_version()
- if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.maxlength > 255:
- e.add(opts, '"%s": %s cannot have a "maxlength" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
+ if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
+ e.add(opts, '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
# Check to see if the related field will clash with any
# existing fields, m2m fields, m2m related objects or related objects
@@ -1406,7 +1406,7 @@ def createcachetable(tablename):
from django.db import backend, connection, transaction, models
fields = (
# "key" is a reserved word in MySQL, so use "cache_key" instead.
- models.CharField(name='cache_key', maxlength=255, unique=True, primary_key=True),
+ models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
models.TextField(name='value'),
models.DateTimeField(name='expires', db_index=True),
)
@@ -1454,6 +1454,10 @@ def run_shell(use_plain=False):
shell.mainloop()
except ImportError:
import code
+ # Set up a dictionary to serve as the environment for the shell, so
+ # that tab completion works on objects that are imported at runtime.
+ # See ticket 5082.
+ imported_objects = {}
try: # Try activating rlcompleter, because it's handy.
import readline
except ImportError:
@@ -1462,8 +1466,9 @@ def run_shell(use_plain=False):
# We don't have to wrap the following import in a 'try', because
# we already know 'readline' was imported successfully.
import rlcompleter
+ readline.set_completer(rlcompleter.Completer(imported_objects).complete)
readline.parse_and_bind("tab:complete")
- code.interact()
+ code.interact(local=imported_objects)
run_shell.args = '[--plain]'
def dbshell():
@@ -1578,7 +1583,7 @@ def load_data(fixture_labels, verbosity=1):
print "Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir))
try:
- objects = serializers.deserialize(format, fixture)
+ objects = serializers.deserialize(format, fixture)
for obj in objects:
count[0] += 1
models.add(obj.object.__class__)
View
2 django/core/validators.py
@@ -442,7 +442,7 @@ def isValidFloat(field_data, all_data):
try:
float(data)
except ValueError:
- raise ValidationError, ugettext("Please enter a valid floating point number.")
+ raise ValidationError, _("Please enter a valid floating point number.")
class HasAllowableSize(object):
"""
View
6 django/db/backends/ado_mssql/creation.py
@@ -1,8 +1,8 @@
DATA_TYPES = {
'AutoField': 'int IDENTITY (1, 1)',
'BooleanField': 'bit',
- 'CharField': 'varchar(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'smalldatetime',
'DateTimeField': 'smalldatetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
@@ -17,7 +17,7 @@
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)',
'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
- 'SlugField': 'varchar(%(maxlength)s)',
+ 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
View
6 django/db/backends/mysql/creation.py
@@ -5,8 +5,8 @@
DATA_TYPES = {
'AutoField': 'integer AUTO_INCREMENT',
'BooleanField': 'bool',
- 'CharField': 'varchar(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
@@ -21,7 +21,7 @@
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
- 'SlugField': 'varchar(%(maxlength)s)',
+ 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
View
6 django/db/backends/mysql_old/creation.py
@@ -5,8 +5,8 @@
DATA_TYPES = {
'AutoField': 'integer AUTO_INCREMENT',
'BooleanField': 'bool',
- 'CharField': 'varchar(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
@@ -21,7 +21,7 @@
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
- 'SlugField': 'varchar(%(maxlength)s)',
+ 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
View
4 django/db/backends/oracle/creation.py
@@ -8,8 +8,8 @@
DATA_TYPES = {
'AutoField': 'NUMBER(11)',
'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))',
- 'CharField': 'NVARCHAR2(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'VARCHAR2(%(maxlength)s)',
+ 'CharField': 'NVARCHAR2(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
'DateField': 'DATE',
'DateTimeField': 'TIMESTAMP',
'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
View
6 django/db/backends/postgresql/creation.py
@@ -5,8 +5,8 @@
DATA_TYPES = {
'AutoField': 'serial',
'BooleanField': 'boolean',
- 'CharField': 'varchar(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'timestamp with time zone',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
@@ -21,7 +21,7 @@
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
- 'SlugField': 'varchar(%(maxlength)s)',
+ 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
View
6 django/db/backends/sqlite3/creation.py
@@ -4,8 +4,8 @@
DATA_TYPES = {
'AutoField': 'integer',
'BooleanField': 'bool',
- 'CharField': 'varchar(%(maxlength)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'decimal',
@@ -20,7 +20,7 @@
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer unsigned',
'PositiveSmallIntegerField': 'smallint unsigned',
- 'SlugField': 'varchar(%(maxlength)s)',
+ 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
View
2 django/db/backends/sqlite3/introspection.py
@@ -137,7 +137,7 @@ def __getitem__(self, key):
import re
m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
if m:
- return ('CharField', {'maxlength': int(m.group(1))})
+ return ('CharField', {'max_length': int(m.group(1))})
raise KeyError
DATA_TYPES_REVERSE = FlexibleFieldLookupDict()
View
49 django/db/models/fields/__init__.py
@@ -11,6 +11,7 @@
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy, ugettext as _
from django.utils.encoding import smart_unicode, force_unicode, smart_str
+from django.utils.maxlength import LegacyMaxlength
import datetime, os, time
try:
import decimal
@@ -63,6 +64,9 @@ def manipulator_validator_unique(f, opts, self, field_data, all_data):
# getattr(obj, opts.pk.attname)
class Field(object):
+ # Provide backwards compatibility for the maxlength attribute and
+ # argument for this class and all subclasses.
+ __metaclass__ = LegacyMaxlength
# Designates whether empty strings fundamentally are allowed at the
# database level.
@@ -72,15 +76,15 @@ class Field(object):
creation_counter = 0
def __init__(self, verbose_name=None, name=None, primary_key=False,
- maxlength=None, unique=False, blank=False, null=False, db_index=False,
+ max_length=None, unique=False, blank=False, null=False, db_index=False,
core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
prepopulate_from=None, unique_for_date=None, unique_for_month=None,
unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
help_text='', db_column=None, aka=None, db_tablespace=None):
self.name = name
self.verbose_name = verbose_name
self.primary_key = primary_key
- self.maxlength, self.unique = maxlength, unique
+ self.max_length, self.unique = max_length, unique
self.blank, self.null = blank, null
# Oracle treats the empty string ('') as null, so coerce the null
# option whenever '' is a possible value.
@@ -245,8 +249,8 @@ def get_manipulator_field_names(self, name_prefix):
def prepare_field_objs_and_params(self, manipulator, name_prefix):
params = {'validator_list': self.validator_list[:]}
- if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
- params['maxlength'] = self.maxlength
+ 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:
if self.radio_admin:
@@ -377,6 +381,9 @@ def _get_choices(self):
return self._choices
choices = property(_get_choices)
+ def save_form_data(self, instance, data):
+ setattr(instance, self.name, data)
+
def formfield(self, form_class=forms.CharField, **kwargs):
"Returns a django.newforms.Field instance for this database Field."
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
@@ -462,7 +469,7 @@ def to_python(self, value):
return smart_unicode(value)
def formfield(self, **kwargs):
- defaults = {'max_length': self.maxlength}
+ defaults = {'max_length': self.max_length}
defaults.update(kwargs)
return super(CharField, self).formfield(**defaults)
@@ -671,7 +678,7 @@ def formfield(self, **kwargs):
class EmailField(CharField):
def __init__(self, *args, **kwargs):
- kwargs['maxlength'] = 75
+ kwargs['max_length'] = 75
CharField.__init__(self, *args, **kwargs)
def get_internal_type(self):
@@ -693,6 +700,13 @@ def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
Field.__init__(self, verbose_name, name, **kwargs)
+ def get_db_prep_save(self, value):
+ "Returns field's value prepared for saving into a database."
+ # Need to convert UploadedFile objects provided via a form to unicode for database insertion
+ if value is None:
+ 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:
@@ -769,6 +783,19 @@ def get_filename(self, filename):
f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
return os.path.normpath(f)
+ def save_form_data(self, instance, data):
+ if data:
+ getattr(instance, "save_%s_file" % self.name)(os.path.join(self.upload_to, data.filename), data.content, save=False)
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class': forms.FileField}
+ # If a file has been provided previously, then the form doesn't require
+ # that a new file is provided this time.
+ if 'initial' in kwargs:
+ defaults['required'] = False
+ defaults.update(kwargs)
+ return super(FileField, self).formfield(**defaults)
+
class FilePathField(Field):
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
@@ -817,6 +844,10 @@ def save_file(self, new_data, new_object, original_object, change, rel, save=Tru
setattr(new_object, self.height_field, getattr(original_object, self.height_field))
new_object.save()
+ def formfield(self, **kwargs):
+ defaults = {'form_class': forms.ImageField}
+ return super(ImageField, self).formfield(**defaults)
+
class IntegerField(Field):
empty_strings_allowed = False
def get_manipulator_field_objs(self):
@@ -830,7 +861,7 @@ def formfield(self, **kwargs):
class IPAddressField(Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
- kwargs['maxlength'] = 15
+ kwargs['max_length'] = 15
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
@@ -878,7 +909,7 @@ def get_manipulator_field_objs(self):
class SlugField(Field):
def __init__(self, *args, **kwargs):
- kwargs['maxlength'] = kwargs.get('maxlength', 50)
+ 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:
@@ -964,7 +995,7 @@ def formfield(self, **kwargs):
class URLField(CharField):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
- kwargs['maxlength'] = kwargs.get('maxlength', 200)
+ kwargs['max_length'] = kwargs.get('max_length', 200)
if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
self.verify_exists = verify_exists
View
3 django/db/models/fields/related.py
@@ -756,6 +756,9 @@ def value_from_object(self, obj):
"Returns the value of this field in the given model instance."
return getattr(obj, self.attname).all()
+ def save_form_data(self, instance, data):
+ setattr(instance, self.attname, data)
+
def formfield(self, **kwargs):
defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.all()}
defaults.update(kwargs)
View
2 django/newforms/extras/widgets.py
@@ -53,7 +53,7 @@ def render(self, name, value, attrs=None):
return u'\n'.join(output)
- def value_from_datadict(self, data, name):
+ def value_from_datadict(self, data, files, name):
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
if y and m and d:
return '%s-%s-%s' % (y, m, d)
View
56 django/newforms/fields.py
@@ -7,10 +7,10 @@
import time
from django.utils.translation import ugettext
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import StrAndUnicode, smart_unicode
from util import ErrorList, ValidationError
-from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
+from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
try:
from decimal import Decimal, DecimalException
@@ -22,7 +22,7 @@
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
- 'RegexField', 'EmailField', 'URLField', 'BooleanField',
+ 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField',
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
'SplitDateTimeField',
@@ -120,6 +120,7 @@ def clean(self, value):
def widget_attrs(self, widget):
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+ # The HTML attribute is maxlength, not max_length.
return {'maxlength': str(self.max_length)}
class IntegerField(Field):
@@ -347,6 +348,55 @@ def __init__(self, max_length=None, min_length=None, *args, **kwargs):
# It's OK if Django settings aren't configured.
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
+class UploadedFile(StrAndUnicode):
+ "A wrapper for files uploaded in a FileField"
+ def __init__(self, filename, content):
+ self.filename = filename
+ self.content = content
+
+ def __unicode__(self):
+ """
+ The unicode representation is the filename, so that the pre-database-insertion
+ logic can use UploadedFile objects
+ """
+ return self.filename
+
+class FileField(Field):
+ widget = FileInput
+ def __init__(self, *args, **kwargs):
+ super(FileField, self).__init__(*args, **kwargs)
+
+ def clean(self, data):
+ super(FileField, self).clean(data)
+ if not self.required and data in EMPTY_VALUES:
+ return None
+ try:
+ f = UploadedFile(data['filename'], data['content'])
+ except TypeError:
+ raise ValidationError(ugettext(u"No file was submitted. Check the encoding type on the form."))
+ except KeyError:
+ raise ValidationError(ugettext(u"No file was submitted."))
+ if not f.content:
+ raise ValidationError(ugettext(u"The submitted file is empty."))
+ return f
+
+class ImageField(FileField):
+ def clean(self, data):
+ """
+ Checks that the file-upload field data contains a valid image (GIF, JPG,
+ PNG, possibly others -- whatever the Python Imaging Library supports).
+ """
+ f = super(ImageField, self).clean(data)
+ if f is None:
+ return None
+ from PIL import Image
+ from cStringIO import StringIO
+ try:
+ Image.open(StringIO(f.content))
+ except IOError: # Python Imaging Library doesn't recognize it as an image
+ raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
+ return f
+
class URLField(RegexField):
def __init__(self, max_length=None, min_length=None, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
View
13 django/newforms/forms.py
@@ -57,9 +57,10 @@ class BaseForm(StrAndUnicode):
# class is different than Form. See the comments by the Form class for more
# information. Any improvements to the form API should be made to *this*
# class, not to the Form class.
- def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
- self.is_bound = data is not None
+ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None):
+ self.is_bound = data is not None or files is not None
self.data = data or {}
+ self.files = files or {}
self.auto_id = auto_id
self.prefix = prefix
self.initial = initial or {}
@@ -88,7 +89,7 @@ def __getitem__(self, name):
return BoundField(self, field, name)
def _get_errors(self):
- "Returns an ErrorDict for self.data"
+ "Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
@@ -179,10 +180,10 @@ def full_clean(self):
return
self.cleaned_data = {}
for name, field in self.fields.items():
- # value_from_datadict() gets the data from the dictionary.
+ # value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
- value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
+ value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
value = field.clean(value)
self.cleaned_data[name] = value
@@ -283,7 +284,7 @@ def _data(self):
"""
Returns the data for this BoundField, or None if it wasn't given.
"""
- return self.field.widget.value_from_datadict(self.form.data, self.html_name)
+ return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
data = property(_data)
def label_tag(self, contents=None, attrs=None):
View
22 django/newforms/models.py
@@ -34,18 +34,24 @@ def save_instance(form, instance, fields=None, fail_message='saved', commit=True
continue
if fields and f.name not in fields:
continue
- setattr(instance, f.name, cleaned_data[f.name])
- if commit:
- instance.save()
+ f.save_form_data(instance, cleaned_data[f.name])
+ # Wrap up the saving of m2m data as a function
+ def save_m2m():
+ opts = instance.__class__._meta
+ cleaned_data = form.cleaned_data
for f in opts.many_to_many:
if fields and f.name not in fields:
continue
if f.name in cleaned_data:
- setattr(instance, f.attname, cleaned_data[f.name])
- # GOTCHA: If many-to-many data is given and commit=False, the many-to-many
- # data will be lost. This happens because a many-to-many options cannot be
- # set on an object until after it's saved. Maybe we should raise an
- # exception in that case.
+ f.save_form_data(instance, cleaned_data[f.name])
+ if commit:
+ # If we are committing, save the instance and the m2m data immediately
+ instance.save()
+ save_m2m()
+ else:
+ # We're not committing. Add a method to the form to allow deferred
+ # saving of m2m data
+ form.save_m2m = save_m2m
return instance
def make_model_save(model, fields, fail_message):
View
19 django/newforms/widgets.py
@@ -47,7 +47,7 @@ def build_attrs(self, extra_attrs=None, **kwargs):
attrs.update(extra_attrs)
return attrs
- def value_from_datadict(self, data, name):
+ def value_from_datadict(self, data, files, name):
"""
Given a dictionary of data and this widget's name, returns the value
of this widget. Returns None if it's not provided.
@@ -113,14 +113,21 @@ def render(self, name, value, attrs=None, choices=()):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=force_unicode(v), **final_attrs))) for v in value])
- def value_from_datadict(self, data, name):
+ def value_from_datadict(self, data, files, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
class FileInput(Input):
input_type = 'file'
+ def render(self, name, value, attrs=None):
+ return super(FileInput, self).render(name, None, attrs=attrs)
+
+ def value_from_datadict(self, data, files, name):
+ "File widgets take data from FILES, not POST"
+ return files.get(name, None)
+
class Textarea(Widget):
def __init__(self, attrs=None):
# The 'rows' and 'cols' attributes are required for HTML correctness.
@@ -188,7 +195,7 @@ def render(self, name, value, attrs=None, choices=()):
value = u'1'
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
- def value_from_datadict(self, data, name):
+ def value_from_datadict(self, data, files, name):
value = data.get(name, None)
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
@@ -210,7 +217,7 @@ def render(self, name, value, attrs=None, choices=()):
output.append(u'</select>')
return u'\n'.join(output)
- def value_from_datadict(self, data, name):
+ def value_from_datadict(self, data, files, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
@@ -377,8 +384,8 @@ def id_for_label(self, id_):
return id_
id_for_label = classmethod(id_for_label)
- def value_from_datadict(self, data, name):
- return [widget.value_from_datadict(data, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
+ def value_from_datadict(self, data, files, name):
+ return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
def format_output(self, rendered_widgets):
"""
View
116 django/oldforms/__init__.py
@@ -4,6 +4,7 @@
from django.conf import settings
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import smart_unicode, force_unicode
+from django.utils.maxlength import LegacyMaxlength
FORM_FIELD_ID_PREFIX = 'id_'
@@ -302,6 +303,9 @@ class FormField(object):
Subclasses should also implement a render(data) method, which is responsible
for rending the form field in XHTML.
"""
+ # Provide backwards compatibility for the maxlength attribute and
+ # argument for this class and all subclasses.
+ __metaclass__ = LegacyMaxlength
def __str__(self):
return unicode(self).encode('utf-8')
@@ -390,19 +394,19 @@ def get_id(self):
class TextField(FormField):
input_type = "text"
- def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, member_name=None):
+ 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.maxlength = length, maxlength
+ 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.maxlength and len(smart_unicode(data)) > self.maxlength:
+ 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.maxlength) % self.maxlength
+ "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:
@@ -411,12 +415,12 @@ def hasNoNewlines(self, data, form):
def render(self, data):
if data is None:
data = u''
- maxlength = u''
- if self.maxlength:
- maxlength = u'maxlength="%s" ' % self.maxlength
+ max_length = u''
+ if self.max_length:
+ max_length = u'maxlength="%s" ' % self.max_length
return 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), maxlength)
+ self.field_name, self.length, escape(data), max_length)
def html2python(data):
return data
@@ -426,14 +430,14 @@ class PasswordField(TextField):
input_type = "password"
class LargeTextField(TextField):
- def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, maxlength=None):
+ 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 maxlength:
+ if max_length:
self.validator_list.append(self.isValidLength)
- self.maxlength = maxlength
+ self.max_length = max_length
def render(self, data):
if data is None:
@@ -710,12 +714,12 @@ def isValidImage(self, field_data, all_data):
####################
class IntegerField(TextField):
- def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None, member_name=None):
+ 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, maxlength, is_required, validator_list)
+ TextField.__init__(self, field_name, length, max_length, is_required, validator_list)
def isInteger(self, field_data, all_data):
try:
@@ -730,57 +734,57 @@ def html2python(data):
html2python = staticmethod(html2python)
class SmallIntegerField(IntegerField):
- def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=None):
+ 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, maxlength, is_required, 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, maxlength=None, is_required=False, validator_list=None):
+ 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, maxlength, is_required, 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, maxlength=None, is_required=False, validator_list=None):
+ 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, maxlength, is_required, 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, 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)
+ 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)
+ 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:
@@ -789,14 +793,14 @@ def isValidDecimal(self, field_data, all_data):
def html2python(data):
if data == '' or data is None:
return None
- try:
- import decimal
+ try:
+ import decimal
except ImportError:
from django.utils import _decimal as decimal
- try:
- return decimal.Decimal(data)
- except decimal.InvalidOperation, e:
- raise ValueError, e
+ try:
+ return decimal.Decimal(data)
+ except decimal.InvalidOperation, e:
+ raise ValueError, e
html2python = staticmethod(html2python)
####################
@@ -806,10 +810,10 @@ def html2python(data):
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, maxlength=None, is_required=False, validator_list=None):
+ 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.maxlength = length, maxlength
+ self.length, self.max_length = length, max_length
self.is_required = is_required
self.validator_list = [validators.isValidANSIDatetime] + validator_list
@@ -836,7 +840,7 @@ class DateField(TextField):
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, maxlength=10,
+ 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):
@@ -861,7 +865,7 @@ class TimeField(TextField):
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, maxlength=8,
+ 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):
@@ -893,10 +897,10 @@ def html2python(data):
class EmailField(TextField):
"A convenience FormField for validating e-mail addresses"
- def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=None):
+ 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, maxlength=maxlength,
+ 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):
@@ -907,10 +911,10 @@ def isValidEmail(self, field_data, all_data):
class URLField(TextField):
"A convenience FormField for validating URLs"
- def __init__(self, field_name, length=50, maxlength=200, is_required=False, validator_list=None):
+ 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, maxlength=maxlength,
+ 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):
@@ -920,10 +924,10 @@ def isValidURL(self, field_data, all_data):
raise validators.CriticalValidationError, e.messages
class IPAddressField(TextField):
- def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=None):
+ 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, maxlength=maxlength,
+ 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):
@@ -970,7 +974,7 @@ class PhoneNumberField(TextField):
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, maxlength=12,
+ 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):
@@ -984,7 +988,7 @@ class USStateField(TextField):
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, maxlength=2,
+ 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):
@@ -1001,10 +1005,10 @@ def html2python(data):
class CommaSeparatedIntegerField(TextField):
"A convenience FormField for validating comma-separated integer fields"
- def __init__(self, field_name, maxlength=None, is_required=False, validator_list=None):
+ 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, maxlength=maxlength,
+ 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):
View
31 django/template/defaulttags.py
@@ -649,8 +649,8 @@ def do_if(parser, token):
As you can see, the ``if`` tag can take an option ``{% else %}`` clause that
will be displayed if the test fails.
- ``if`` tags may use ``or`` or ``not`` to test a number of variables or to
- negate a given variable::
+ ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of
+ variables or to negate a given variable::
{% if not athlete_list %}
There are no athletes.
@@ -660,19 +660,32 @@ def do_if(parser, token):
There are some athletes or some coaches.
{% endif %}
+ {% if athlete_list and coach_list %}
+ Both atheletes and coaches are available.
+ {% endif %}
+
{% if not athlete_list or coach_list %}
There are no athletes, or there are some coaches.
{% endif %}
- For simplicity, ``if`` tags do not allow ``and`` clauses. Use nested ``if``
- tags instead::
+ {% if athlete_list and not coach_list %}
+ There are some athletes and absolutely no coaches.
+ {% endif %}
- {% if athlete_list %}
- {% if coach_list %}
- Number of athletes: {{ athlete_list|count }}.
- Number of coaches: {{ coach_list|count }}.
- {% endif %}
+ ``if`` tags do not allow ``and`` and ``or`` clauses with the same
+ tag, because the order of logic would be ambigous. For example,
+ this is invalid::
+
+ {% if athlete_list and coach_list or cheerleader_list %}
+
+ If you need to combine and and or to do advanced logic, just use
+ nested if tags. For example:
+
+ {% if athlete_list %}
+ {% if coach_list or cheerleader_list %}
+ We have athletes, and either coaches or cheerleaders!
{% endif %}
+ {% endif %}
"""
bits = token.contents.split()
del bits[0]
View
1 django/utils/encoding.py
@@ -1,6 +1,5 @@
import types
import urllib
-from django.conf import settings
from django.utils.functional import Promise
class StrAndUnicode(object):
View
67 django/utils/maxlength.py
@@ -0,0 +1,67 @@
+"""
+Utilities for providing backwards compatibility for the maxlength argument,
+which has been replaced by max_length, see ticket #2101.
+"""
+
+from warnings import warn
+
+def get_maxlength(self):
+ return self.max_length
+
+def set_maxlength(self, value):
+ self.max_length = value
+
+def legacy_maxlength(max_length, maxlength):
+ """
+ Consolidates max_length and maxlength, providing backwards compatibilty
+ for the legacy "maxlength" argument.
+ If one of max_length or maxlength is given, then that value is returned.
+ If both are given, a TypeError is raised.
+ If maxlength is used at all, a deprecation warning is issued.
+ """
+ if maxlength is not None:
+ warn("maxlength is deprecated, use max_length instead.",
+ PendingDeprecationWarning,
+ stacklevel=3)
+ if max_length is not None:
+ raise TypeError("field can not take both the max_length"
+ " argument and the legacy maxlength argument.")
+ max_length = maxlength
+ return max_length
+
+def remove_maxlength(func):
+ """
+ A decorator to be used on a class's __init__ that provides backwards
+ compatibilty for the legacy "maxlength" keyword argument, i.e.
+ name = models.CharField(maxlength=20)
+ It does this by changing the passed "maxlength" keyword argument
+ (if it exists) into a "max_length" keyword argument.
+ """
+ def inner(self, *args, **kwargs):
+ max_length = kwargs.get('max_length', None)
+ # pop maxlength because we don't want this going to __init__.
+ maxlength = kwargs.pop('maxlength', None)
+ max_length = legacy_maxlength(max_length, maxlength)
+ # Only set the max_length keyword argument if we got a value back.
+ if max_length is not None:
+ kwargs['max_length'] = max_length
+ func(self, *args, **kwargs)
+ return inner
+
+# This metaclass is used in two places, and should be removed when legacy
+# support for maxlength is dropped.
+# * oldforms.FormField
+# * db.models.fields.Field
+
+class LegacyMaxlength(type):
+ """
+ Metaclass for providing backwards compatibility support for the
+ "maxlength" keyword argument.
+ """
+
+ def __init__(cls, name, bases, attrs):
+ super(LegacyMaxlength, cls).__init__(name, bases, attrs)
+ # Decorate the class's __init__ to remove any maxlength keyword.
+ cls.__init__ = remove_maxlength(cls.__init__)
+ # Support accessing and setting to the legacy maxlength attribute.
+ cls.maxlength = property(get_maxlength, set_maxlength)
View
2 docs/add_ons.txt
@@ -214,7 +214,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
See the `syndication documentation`_.
-.. _syndication documentation: ../syndication/
+.. _syndication documentation: ../syndication_feeds/
Other add-ons
=============
View
5 docs/api_stability.txt
@@ -82,9 +82,6 @@ that 90% of Django can be considered forwards-compatible at this point.
That said, these APIs should *not* be considered stable, and are likely to
change:
- - `Forms and validation`_ will most likely be completely rewritten to
- deemphasize Manipulators in favor of validation-aware models.
-
- `Serialization`_ is under heavy development; changes are likely.
- The `authentication`_ framework is changing to be far more flexible, and
@@ -114,7 +111,7 @@ change:
.. _sending email: ../email/
.. _sessions: ../sessions/
.. _settings: ../settings/
-.. _syndication: ../syndication/
+.. _syndication: ../syndication_feeds/
.. _template language: ../templates/
.. _transactions: ../transactions/
.. _url dispatch: ../url_dispatch/
View
2 docs/authentication.txt
@@ -234,7 +234,7 @@ the setting and checking of these values behind the scenes.
Previous Django versions, such as 0.90, used simple MD5 hashes without password
salts. For backwards compatibility, those are still supported; they'll be
-converted automatically to the new style the first time ``check_password()``
+converted automatically to the new style the first time ``User.check_password()``
works correctly for a given user.
Anonymous users
View
20 docs/contributing.txt
@@ -340,32 +340,32 @@ Model style
Do this::
class Person(models.Model):
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=40)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=40)
Don't do this::
class Person(models.Model):
- FirstName = models.CharField(maxlength=20)
- Last_Name = models.CharField(maxlength=40)
+ FirstName = models.CharField(max_length=20)
+ Last_Name = models.CharField(max_length=40)
* The ``class Meta`` should appear *after* the fields are defined, with
a single blank line separating the fields and the class definition.
Do this::
class Person(models.Model):
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=40)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=40)
class Meta:
verbose_name_plural = 'people'
Don't do this::
class Person(models.Model):
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=40)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=40)
class Meta:
verbose_name_plural = 'people'
@@ -375,8 +375,8 @@ Model style
class Meta:
verbose_name_plural = 'people'
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=40)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=40)
* The order of model inner classes and standard methods should be as
follows (noting that these are not all required):
View
2 docs/databases.txt
@@ -124,7 +124,7 @@ Several other MySQLdb connection options may be useful, such as ``ssl``,
``use_unicode``, ``init_command``, and ``sql_mode``. Consult the
`MySQLdb documentation`_ for more details.
-.. _settings documentation: http://www.djangoproject.com/documentation/settings/#database-engine
+.. _settings documentation: ../settings/#database-engine
.. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html
.. _MySQLdb documentation: http://mysql-python.sourceforge.net/
View
14 docs/db-api.txt
@@ -12,22 +12,22 @@ Throughout this reference, we'll refer to the following models, which comprise
a weblog application::
class Blog(models.Model):
- name = models.CharField(maxlength=100)
+ name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
- headline = models.CharField(maxlength=255)
+ headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
@@ -1503,7 +1503,7 @@ precede the definition of any keyword arguments. For example::
See the `OR lookups examples page`_ for more examples.
-.. _OR lookups examples page: http://www.djangoproject.com/documentation/models/or_lookups/
+.. _OR lookups examples page: ../models/or_lookups/
Related objects
===============
@@ -1806,8 +1806,8 @@ following model::
('F', 'Female'),
)
class Person(models.Model):
- name = models.CharField(maxlength=20)
- gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
+ name = models.CharField(max_length=20)
+ gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
...each ``Person`` instance will have a ``get_gender_display()`` method. Example::