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() +
  • Username:
  • +
  • Password:
  • +>>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +
  • Username:
  • +
  • Password:
  • +>>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +
  • Username:
  • +
  • Password:
  • + +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,