Ticket #3132: newforms-prefix.diff

File newforms-prefix.diff, 5.4 KB (added by jkocherhans, 9 years ago)

Initial support and minimal tests. Needs more work, but feel free to nitpick.

  • django/newforms/forms.py

     
    3636    "A collection of Fields, plus their associated data."
    3737    __metaclass__ = DeclarativeFieldsMetaclass
    3838
    39     def __init__(self, data=None, auto_id='id_%s'): # TODO: prefix stuff
     39    def __init__(self, data=None, auto_id='id_%s', prefix=None):
    4040        self.ignore_errors = data is None
    4141        self.data = data or {}
    4242        self.auto_id = auto_id
     43        self.prefix = prefix
    4344        self.clean_data = None # Stores the data after clean() has been called.
    4445        self.__errors = None # Stores the errors after clean() has been called.
    4546
     
    4849
    4950    def __iter__(self):
    5051        for name, field in self.fields.items():
     52            # TODO: This prefix code probably shouldn't be in so many places.
     53            if self.prefix:
     54                name = "%s.%s" % (self.prefix, name)
    5155            yield BoundField(self, field, name)
    5256
    5357    def __getitem__(self, name):
     
    5660            field = self.fields[name]
    5761        except KeyError:
    5862            raise KeyError('Key %r not found in Form' % name)
     63        # TODO: This prefix code probably shouldn't be in so many places.
     64        if self.prefix:
     65            name = "%s.%s" % (self.prefix, name)
    5966        return BoundField(self, field, name)
    6067
    6168    def _errors(self):
     
    7784        top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
    7885        output, hidden_fields = [], []
    7986        for name, field in self.fields.items():
     87            # TODO: This prefix code probably shouldn't be in so many places.
     88            if self.prefix:
     89                name = "%s.%s" % (self.prefix, name)
    8090            bf = BoundField(self, field, name)
    8191            bf_errors = bf.errors # Cache in local variable.
    8292            if bf.is_hidden:
     
    129139            self.__errors = errors
    130140            return
    131141        for name, field in self.fields.items():
     142            # TODO: This prefix code probably shouldn't be in so many places.
     143            if self.prefix:
     144                prefixed_name = "%s.%s" % (self.prefix, name)
     145            else:
     146                prefixed_name = name
    132147            # value_from_datadict() gets the data from the dictionary.
    133148            # Each widget type knows how to retrieve its own data, because some
    134149            # widgets split data over several HTML fields.
    135             value = field.widget.value_from_datadict(self.data, name)
     150            value = field.widget.value_from_datadict(self.data, prefixed_name)
    136151            try:
    137152                value = field.clean(value)
    138153                self.clean_data[name] = value
  • tests/regressiontests/forms/tests.py

     
    19241924<li>Password1: <input type="password" name="password1" /></li>
    19251925<li>Password (again): <input type="password" name="password2" /></li>
    19261926
     1927# Forms with prefixes #########################################################
     1928
     1929Sometimes you'd like to have multiple forms display on the same html page, or
     1930maybe just multiple copies of the same form. We can accomplish this with form
     1931prefixes.
     1932
     1933>>> class Person(Form):
     1934...     first_name = CharField()
     1935...     last_name = CharField()
     1936...     birthday = DateField()
     1937
     1938Pass a dictionary to a Form's __init__(), but also pass in the keyword argument
     1939'prefix'. This value will be prepended to each html form field name. One way to
     1940think about this is "namespaces for html forms". Notice that in the data
     1941argument, each field's key has the prefix, in this case 'person1', prepended
     1942to the actual field name.
     1943>>> data = {
     1944...     'person1.first_name': u'John',
     1945...     'person1.last_name': u'Lennon',
     1946...     'person1.birthday': u'1940-10-9'
     1947... }
     1948>>> p = Person(data, prefix='person1')
     1949>>> print p['first_name']
     1950<input type="text" name="person1.first_name" value="John" id="id_person1.first_name" />
     1951>>> print p['last_name']
     1952<input type="text" name="person1.last_name" value="Lennon" id="id_person1.last_name" />
     1953>>> print p['birthday']
     1954<input type="text" name="person1.birthday" value="1940-10-9" id="id_person1.birthday" />
     1955>>> p.errors
     1956{}
     1957>>> p.is_valid()
     1958True
     1959
     1960This is pretty unremarkable in and of itself, but let's create some data that
     1961contains info for two different people.
     1962>>> data = {
     1963...     'person1.first_name': u'John',
     1964...     'person1.last_name': u'Lennon',
     1965...     'person1.birthday': u'1940-10-9',
     1966...     'person2.first_name': u'Jim',
     1967...     'person2.last_name': u'Morrison',
     1968...     'person2.birthday': u'1943-12-8'
     1969... }
     1970
     1971If we use the correct prefix argument, we can create two different forms that
     1972will only use and validate the data for fields with a matching prefix.
     1973>>> p1 = Person(data, prefix='person1')
     1974>>> p1.is_valid()
     1975True
     1976>>> p1.clean_data
     1977{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
     1978
     1979>>> p2 = Person(data, prefix='person2')
     1980>>> p2.is_valid()
     1981True
     1982>>> p2.clean_data
     1983{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
     1984
    19271985# Basic form processing in a view #############################################
    19281986
    19291987>>> from django.template import Template, Context
Back to Top