diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index feef889d83..22ca2d0bab 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -36,10 +36,11 @@ class Form(StrAndUnicode):
"A collection of Fields, plus their associated data."
__metaclass__ = DeclarativeFieldsMetaclass
- def __init__(self, data=None, auto_id='id_%s'): # TODO: prefix stuff
+ def __init__(self, data=None, auto_id='id_%s', prefix=None):
self.ignore_errors = data is None
self.data = data or {}
self.auto_id = auto_id
+ self.prefix = prefix
self.clean_data = None # Stores the data after clean() has been called.
self.__errors = None # Stores the errors after clean() has been called.
@@ -72,6 +73,15 @@ class Form(StrAndUnicode):
"""
return not self.ignore_errors and not bool(self.errors)
+ def add_prefix(self, field_name):
+ """
+ Returns the field name with a prefix appended, if this Form has a
+ prefix set.
+
+ Subclasses may wish to override.
+ """
+ return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
+
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
@@ -132,7 +142,7 @@ class Form(StrAndUnicode):
# value_from_datadict() gets the data from the dictionary.
# 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, name)
+ value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
try:
value = field.clean(value)
self.clean_data[name] = value
@@ -163,7 +173,7 @@ class BoundField(StrAndUnicode):
def __init__(self, form, field, name):
self.form = form
self.field = field
- self.name = name
+ self.name = form.add_prefix(name)
self.label = self.field.label or pretty_name(name)
def __unicode__(self):
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 8b71bf6adc..825b44862d 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -1924,6 +1924,91 @@ underscores converted to spaces, and the initial letter capitalized.
Password1:
Password (again):
+# Forms with prefixes #########################################################
+
+Sometimes it's necessary to have multiple forms display on the same HTML page,
+or multiple copies of the same form. We can accomplish this with form prefixes.
+Pass the keyword argument 'prefix' to the Form constructor to use this feature.
+This value will be prepended to each HTML form field name. One way to think
+about this is "namespaces for HTML forms". Notice that in the data argument,
+each field's key has the prefix, in this case 'person1', prepended to the
+actual field name.
+>>> class Person(Form):
+... first_name = CharField()
+... last_name = CharField()
+... birthday = DateField()
+>>> data = {
+... 'person1-first_name': u'John',
+... 'person1-last_name': u'Lennon',
+... 'person1-birthday': u'1940-10-9'
+... }
+>>> p = Person(data, prefix='person1')
+>>> print p.as_ul()
+
+
+
+>>> print p['first_name']
+
+>>> print p['last_name']
+
+>>> print p['birthday']
+
+>>> p.errors
+{}
+>>> p.is_valid()
+True
+
+This is pretty unremarkable in and of itself, but let's create some data that
+contains info for two different people.
+>>> data = {
+... 'person1-first_name': u'John',
+... 'person1-last_name': u'Lennon',
+... 'person1-birthday': u'1940-10-9',
+... 'person2-first_name': u'Jim',
+... 'person2-last_name': u'Morrison',
+... 'person2-birthday': u'1943-12-8'
+... }
+
+If we use the correct prefix argument, we can create two different forms that
+will only use and validate the data for fields with a matching prefix.
+>>> p1 = Person(data, prefix='person1')
+>>> p1.is_valid()
+True
+>>> p1.clean_data
+{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+
+>>> p2 = Person(data, prefix='person2')
+>>> p2.is_valid()
+True
+>>> p2.clean_data
+{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
+
+By default, forms append a hyphen between the prefix and the field name, but a
+form can alter that behavior by implementing the add_prefix() method. This
+method takes a field name and returns the prefixed field, according to
+self.prefix.
+>>> class Person(Form):
+... first_name = CharField()
+... last_name = CharField()
+... birthday = DateField()
+... def add_prefix(self, field_name):
+... return self.prefix and '%s-prefix-%s' % (self.prefix, field_name) or field_name
+>>> p = Person(prefix='foo')
+>>> print p.as_ul()
+
+
+
+>>> data = {
+... 'foo-prefix-first_name': u'John',
+... 'foo-prefix-last_name': u'Lennon',
+... 'foo-prefix-birthday': u'1940-10-9'
+... }
+>>> p = Person(data, prefix='foo')
+>>> p.is_valid()
+True
+>>> p.clean_data
+{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+
# Basic form processing in a view #############################################
>>> from django.template import Template, Context