Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

newforms: Added Field.widget_attrs() hook, which lets a Field designa…

…te HTML attributes to use in its widget. Implemented CharField.widget_attrs(), which sets the HTML maxlength attribute for <input type='text'> and <input type='password'>. Thanks for the idea, Gary Doades

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4187 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f10a9105774d95376008d9b91dfe55ba11f0971d 1 parent 4e72dc8
Adrian Holovaty authored December 08, 2006
22  django/newforms/fields.py
@@ -4,7 +4,7 @@
4 4
 
5 5
 from django.utils.translation import gettext
6 6
 from util import ValidationError, smart_unicode
7  
-from widgets import TextInput, CheckboxInput, Select, SelectMultiple
  7
+from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
8 8
 import datetime
9 9
 import re
10 10
 import time
@@ -37,6 +37,12 @@ def __init__(self, required=True, widget=None):
37 37
         widget = widget or self.widget
38 38
         if isinstance(widget, type):
39 39
             widget = widget()
  40
+
  41
+        # Hook into self.widget_attrs() for any Field-specific HTML attributes.
  42
+        extra_attrs = self.widget_attrs(widget)
  43
+        if extra_attrs:
  44
+            widget.attrs.update(extra_attrs)
  45
+
40 46
         self.widget = widget
41 47
 
42 48
         # Increase the creation counter, and save our local copy.
@@ -54,10 +60,18 @@ def clean(self, value):
54 60
             raise ValidationError(gettext(u'This field is required.'))
55 61
         return value
56 62
 
  63
+    def widget_attrs(self, widget):
  64
+        """
  65
+        Given a Widget instance (*not* a Widget class), returns a dictionary of
  66
+        any HTML attributes that should be added to the Widget, based on this
  67
+        Field.
  68
+        """
  69
+        return {}
  70
+
57 71
 class CharField(Field):
58 72
     def __init__(self, max_length=None, min_length=None, required=True, widget=None):
59  
-        Field.__init__(self, required, widget)
60 73
         self.max_length, self.min_length = max_length, min_length
  74
+        Field.__init__(self, required, widget)
61 75
 
62 76
     def clean(self, value):
63 77
         "Validates max_length and min_length. Returns a Unicode object."
@@ -70,6 +84,10 @@ def clean(self, value):
70 84
             raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
71 85
         return value
72 86
 
  87
+    def widget_attrs(self, widget):
  88
+        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
  89
+            return {'maxlength': str(self.max_length)}
  90
+
73 91
 class IntegerField(Field):
74 92
     def clean(self, value):
75 93
         """
51  tests/regressiontests/forms/tests.py
@@ -1736,7 +1736,7 @@
1736 1736
 >>> f = UserRegistration({})
1737 1737
 >>> print f.as_table()
1738 1738
 <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
1739  
-<tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
  1739
+<tr><td>Username:</td><td><input type="text" name="username" maxlength="10" /></td></tr>
1740 1740
 <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
1741 1741
 <tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
1742 1742
 <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
@@ -1748,12 +1748,12 @@
1748 1748
 {'__all__': [u'Please make sure your passwords match.']}
1749 1749
 >>> print f.as_table()
1750 1750
 <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
1751  
-<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
  1751
+<tr><td>Username:</td><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
1752 1752
 <tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
1753 1753
 <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
1754 1754
 >>> print f.as_ul()
1755 1755
 <li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
1756  
-<li>Username: <input type="text" name="username" value="adrian" /></li>
  1756
+<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
1757 1757
 <li>Password1: <input type="password" name="password1" value="foo" /></li>
1758 1758
 <li>Password2: <input type="password" name="password2" value="bar" /></li>
1759 1759
 >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
@@ -1881,6 +1881,33 @@
1881 1881
 <tr><td>Field13:</td><td><input type="text" name="field13" /></td></tr>
1882 1882
 <tr><td>Field14:</td><td><input type="text" name="field14" /></td></tr>
1883 1883
 
  1884
+Some Field classes have an effect on the HTML attributes of their associated
  1885
+Widget. If you set max_length in a CharField and its associated widget is
  1886
+either a TextInput or PasswordInput, then the widget's rendered HTML will
  1887
+include the "maxlength" attribute.
  1888
+>>> class UserRegistration(Form):
  1889
+...    username = CharField(max_length=10)                   # uses TextInput by default
  1890
+...    password = CharField(max_length=10, widget=PasswordInput)
  1891
+...    realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test
  1892
+...    address = CharField()                                 # no max_length defined here
  1893
+>>> p = UserRegistration()
  1894
+>>> print p.as_ul()
  1895
+<li>Username: <input type="text" name="username" maxlength="10" /></li>
  1896
+<li>Password: <input type="password" name="password" maxlength="10" /></li>
  1897
+<li>Realname: <input type="text" name="realname" maxlength="10" /></li>
  1898
+<li>Address: <input type="text" name="address" /></li>
  1899
+
  1900
+If you specify a custom "attrs" that includes the "maxlength" attribute,
  1901
+the Field's max_length attribute will override whatever "maxlength" you specify
  1902
+in "attrs".
  1903
+>>> class UserRegistration(Form):
  1904
+...    username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20}))
  1905
+...    password = CharField(max_length=10, widget=PasswordInput)
  1906
+>>> p = UserRegistration()
  1907
+>>> print p.as_ul()
  1908
+<li>Username: <input type="text" name="username" maxlength="10" /></li>
  1909
+<li>Password: <input type="password" name="password" maxlength="10" /></li>
  1910
+
1884 1911
 # Basic form processing in a view #############################################
1885 1912
 
1886 1913
 >>> from django.template import Template, Context
@@ -1906,7 +1933,7 @@
1906 1933
 >>> print my_function('GET', {})
1907 1934
 <form action="" method="post">
1908 1935
 <table>
1909  
-<tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
  1936
+<tr><td>Username:</td><td><input type="text" name="username" maxlength="10" /></td></tr>
1910 1937
 <tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
1911 1938
 <tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
1912 1939
 </table>
@@ -1919,7 +1946,7 @@
1919 1946
 <table>
1920 1947
 <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
1921 1948
 <tr><td colspan="2"><ul class="errorlist"><li>Ensure this value has at most 10 characters.</li></ul></td></tr>
1922  
-<tr><td>Username:</td><td><input type="text" name="username" value="this-is-a-long-username" /></td></tr>
  1949
+<tr><td>Username:</td><td><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
1923 1950
 <tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
1924 1951
 <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
1925 1952
 </table>
@@ -1954,14 +1981,14 @@
1954 1981
 ... </form>''')
1955 1982
 >>> print t.render(Context({'form': UserRegistration()}))
1956 1983
 <form action="">
1957  
-<p><label>Your username: <input type="text" name="username" /></label></p>
  1984
+<p><label>Your username: <input type="text" name="username" maxlength="10" /></label></p>
1958 1985
 <p><label>Password: <input type="password" name="password1" /></label></p>
1959 1986
 <p><label>Password (again): <input type="password" name="password2" /></label></p>
1960 1987
 <input type="submit" />
1961 1988
 </form>
1962 1989
 >>> print t.render(Context({'form': UserRegistration({'username': 'django'})}))
1963 1990
 <form action="">
1964  
-<p><label>Your username: <input type="text" name="username" value="django" /></label></p>
  1991
+<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
1965 1992
 <ul class="errorlist"><li>This field is required.</li></ul><p><label>Password: <input type="password" name="password1" /></label></p>
1966 1993
 <ul class="errorlist"><li>This field is required.</li></ul><p><label>Password (again): <input type="password" name="password2" /></label></p>
1967 1994
 <input type="submit" />
@@ -1977,7 +2004,7 @@
1977 2004
 ... </form>''')
1978 2005
 >>> print t.render(Context({'form': UserRegistration()}))
1979 2006
 <form action="">
1980  
-<p><label>Username: <input type="text" name="username" /></label></p>
  2007
+<p><label>Username: <input type="text" name="username" maxlength="10" /></label></p>
1981 2008
 <p><label>Password1: <input type="password" name="password1" /></label></p>
1982 2009
 <p><label>Password2: <input type="password" name="password2" /></label></p>
1983 2010
 <input type="submit" />
@@ -1995,14 +2022,14 @@
1995 2022
 ... </form>''')
1996 2023
 >>> print t.render(Context({'form': UserRegistration()}))
1997 2024
 <form action="">
1998  
-<p>Username: <input type="text" name="username" /></p>
  2025
+<p>Username: <input type="text" name="username" maxlength="10" /></p>
1999 2026
 <p>Password1: <input type="password" name="password1" /></p>
2000 2027
 <p>Password2: <input type="password" name="password2" /></p>
2001 2028
 <input type="submit" />
2002 2029
 </form>
2003 2030
 >>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
2004 2031
 <form action="">
2005  
-<p><label for="id_username">Username</label>: <input type="text" name="username" id="id_username" /></p>
  2032
+<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p>
2006 2033
 <p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p>
2007 2034
 <p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p>
2008 2035
 <input type="submit" />
@@ -2020,7 +2047,7 @@
2020 2047
 ... </form>''')
2021 2048
 >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
2022 2049
 <form action="">
2023  
-<p><label>Your username: <input type="text" name="username" value="django" /></label></p>
  2050
+<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
2024 2051
 <p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
2025 2052
 <p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
2026 2053
 <input type="submit" />
@@ -2035,7 +2062,7 @@
2035 2062
 >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
2036 2063
 <form action="">
2037 2064
 <ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
2038  
-<p><label>Your username: <input type="text" name="username" value="django" /></label></p>
  2065
+<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
2039 2066
 <p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
2040 2067
 <p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
2041 2068
 <input type="submit" />

0 notes on commit f10a910

Please sign in to comment.
Something went wrong with that request. Please try again.