diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index fada75fe5de..546f21c2074 100644
--- a/django/newforms/fields.py
+++ b/django/newforms/fields.py
@@ -4,7 +4,7 @@ Field classes
from django.utils.translation import gettext
from util import ValidationError, smart_unicode
-from widgets import TextInput, CheckboxInput, Select, SelectMultiple
+from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
import datetime
import re
import time
@@ -37,6 +37,12 @@ class Field(object):
widget = widget or self.widget
if isinstance(widget, type):
widget = widget()
+
+ # Hook into self.widget_attrs() for any Field-specific HTML attributes.
+ extra_attrs = self.widget_attrs(widget)
+ if extra_attrs:
+ widget.attrs.update(extra_attrs)
+
self.widget = widget
# Increase the creation counter, and save our local copy.
@@ -54,10 +60,18 @@ class Field(object):
raise ValidationError(gettext(u'This field is required.'))
return value
+ def widget_attrs(self, widget):
+ """
+ Given a Widget instance (*not* a Widget class), returns a dictionary of
+ any HTML attributes that should be added to the Widget, based on this
+ Field.
+ """
+ return {}
+
class CharField(Field):
def __init__(self, max_length=None, min_length=None, required=True, widget=None):
- Field.__init__(self, required, widget)
self.max_length, self.min_length = max_length, min_length
+ Field.__init__(self, required, widget)
def clean(self, value):
"Validates max_length and min_length. Returns a Unicode object."
@@ -70,6 +84,10 @@ class CharField(Field):
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
return value
+ def widget_attrs(self, widget):
+ if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+ return {'maxlength': str(self.max_length)}
+
class IntegerField(Field):
def clean(self, value):
"""
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 779db975288..dc4db1a1336 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -1736,7 +1736,7 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f = UserRegistration({})
>>> print f.as_table()
This field is required.
-
Username:
+
Username:
This field is required.
Password1:
This field is required.
@@ -1748,12 +1748,12 @@ Form.clean() is required to return a dictionary of all clean data.
{'__all__': [u'Please make sure your passwords match.']}
>>> print f.as_table()
Please make sure your passwords match.
-
Username:
+
Username:
Password1:
Password2:
>>> print f.as_ul()
Please make sure your passwords match.
-
Username:
+
Username:
Password1:
Password2:
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
@@ -1881,6 +1881,33 @@ A Form's fields are displayed in the same order in which they were defined.
Field13:
Field14:
+Some Field classes have an effect on the HTML attributes of their associated
+Widget. If you set max_length in a CharField and its associated widget is
+either a TextInput or PasswordInput, then the widget's rendered HTML will
+include the "maxlength" attribute.
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10) # uses TextInput by default
+... password = CharField(max_length=10, widget=PasswordInput)
+... realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test
+... address = CharField() # no max_length defined here
+>>> p = UserRegistration()
+>>> print p.as_ul()
+
Username:
+
Password:
+
Realname:
+
Address:
+
+If you specify a custom "attrs" that includes the "maxlength" attribute,
+the Field's max_length attribute will override whatever "maxlength" you specify
+in "attrs".
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20}))
+... password = CharField(max_length=10, widget=PasswordInput)
+>>> p = UserRegistration()
+>>> print p.as_ul()
+
Username:
+
Password:
+
# Basic form processing in a view #############################################
>>> from django.template import Template, Context
@@ -1906,7 +1933,7 @@ Case 1: GET (an empty form, with no errors).
>>> print my_function('GET', {})
''')
>>> print t.render(Context({'form': UserRegistration()}))
>>> print t.render(Context({'form': UserRegistration({'username': 'django'})}))
''')
>>> print t.render(Context({'form': UserRegistration()}))
''')
>>> print t.render(Context({'form': UserRegistration()}))
>>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
''')
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))