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