diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index 4cc6cb4ebd..e1cc566b3b 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -39,11 +39,12 @@ 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):
+ def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
self.is_bound = data is not None
self.data = data or {}
self.auto_id = auto_id
self.prefix = prefix
+ self.initial = initial or {}
self.__errors = None # Stores the errors after clean() has been called.
def __unicode__(self):
@@ -218,7 +219,7 @@ class BoundField(StrAndUnicode):
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
attrs['id'] = auto_id
if not self.form.is_bound:
- data = self.field.initial
+ data = self.form.initial.get(self.name, self.field.initial)
else:
data = self.data
return widget.render(self.html_name, data, attrs=attrs)
diff --git a/docs/newforms.txt b/docs/newforms.txt
index 0b432e1b36..b4da35ecd4 100644
--- a/docs/newforms.txt
+++ b/docs/newforms.txt
@@ -721,6 +721,53 @@ validation if a particular field's value is not given. ``initial`` values are
The ``widget`` argument lets you specify a ``Widget`` class to use when
rendering this ``Field``. See _`Widgets` below for more information.
+Dynamic initial values
+----------------------
+
+The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
+initial value for a ``Field`` -- but what if you want to declare the initial
+value at runtime? For example, you might want to fill in a ``username`` field
+with the username of the current session.
+
+To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
+if given, should be a dictionary mapping field names to initial values. Only
+include the fields for which you're specifying an initial value; it's not
+necessary to include every field in your form. For example::
+
+ >>> class CommentForm(forms.Form):
+ ... name = forms.CharField()
+ ... url = forms.URLField()
+ ... comment = forms.CharField()
+ >>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
+ >>> print f
+
Name:
+
Url:
+
Comment:
+ >>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
+ >>> print f
+
Name:
+
Url:
+
Comment:
+
+Just like the ``initial`` parameter to ``Field``, these values are only
+displayed for unbound forms, and they're not used as fallback values if a
+particular value isn't provided.
+
+Finally, note that if a ``Field`` defines ``initial`` *and* you include
+``initial`` when instantiating the ``Form``, then the latter ``initial`` will
+have precedence. In this example, ``initial`` is provided both at the field
+level and at the form instance level, and the latter gets precedence::
+
+ >>> class CommentForm(forms.Form):
+ ... name = forms.CharField(initial='class')
+ ... url = forms.URLField()
+ ... comment = forms.CharField()
+ >>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
+ >>> print f
+
Name:
+
Url:
+
Comment:
+
More coming soon
================
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 59519c90be..d1202944d0 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -2215,6 +2215,61 @@ validation error rather than using the initial value for 'username'.
>>> p.is_valid()
False
+# Dynamic initial data ########################################################
+
+The previous technique dealt with "hard-coded" initial data, but it's also
+possible to specify initial data after you've already created the Form class
+(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This
+should be a dictionary containing initial values for one or more fields in the
+form, keyed by field name.
+
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10)
+... password = CharField(widget=PasswordInput)
+
+Here, we're not submitting any data, so the initial value will be displayed.
+>>> p = UserRegistration(initial={'username': 'django'}, auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+>>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+
+The 'initial' parameter is meaningless if you pass data.
+>>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False)
+>>> print p.as_ul()
+
+
+A dynamic 'initial' value is *not* used as a fallback if data is not provided.
+In this example, we don't provide a value for 'username', and the form raises a
+validation error rather than using the initial value for 'username'.
+>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
+>>> p.errors
+{'username': [u'This field is required.']}
+>>> p.is_valid()
+False
+
+If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
+then the latter will get precedence.
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10, initial='django')
+... password = CharField(widget=PasswordInput)
+>>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+
# Forms with prefixes #########################################################
Sometimes it's necessary to have multiple forms display on the same HTML page,