Skip to content

Commit

Permalink
Fixed #3334 -- Changed newforms Form class construction so that appen…
Browse files Browse the repository at this point in the history
…ding to (or altering) self.fields affects only the instance, not the class. As a consequence, self.fields is created in Form.__init__(). The form metaclass now creates a variable self.base_fields instead of self.fields.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4437 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
adrianholovaty committed Jan 27, 2007
1 parent c0e0141 commit c93686c
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
13 changes: 11 additions & 2 deletions django/newforms/forms.py
Expand Up @@ -26,12 +26,15 @@ def __init__(self, data=None):
self.keyOrder = [d[0] for d in data] self.keyOrder = [d[0] for d in data]
dict.__init__(self, dict(data)) dict.__init__(self, dict(data))


def copy(self):
return SortedDictFromList(self.items())

class DeclarativeFieldsMetaclass(type): class DeclarativeFieldsMetaclass(type):
"Metaclass that converts Field attributes to a dictionary called 'fields'." "Metaclass that converts Field attributes to a dictionary called 'base_fields'."
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
attrs['fields'] = SortedDictFromList(fields) attrs['base_fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)


class BaseForm(StrAndUnicode): class BaseForm(StrAndUnicode):
Expand All @@ -46,6 +49,12 @@ def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
self.prefix = prefix self.prefix = prefix
self.initial = initial or {} self.initial = initial or {}
self.__errors = None # Stores the errors after clean() has been called. self.__errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
# fields. Because a particular *instance* of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = self.base_fields.copy()


def __unicode__(self): def __unicode__(self):
return self.as_table() return self.as_table()
Expand Down
6 changes: 3 additions & 3 deletions django/newforms/models.py
Expand Up @@ -64,7 +64,7 @@ def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfiel
if formfield: if formfield:
field_list.append((f.name, formfield)) field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list) fields = SortedDictFromList(field_list)
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'save': model_save}) return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})


def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
""" """
Expand All @@ -87,9 +87,9 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw
field_list.append((f.name, formfield)) field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list) fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,), return type(opts.object_name + 'InstanceForm', (form,),
{'fields': fields, '_model': model, 'save': make_instance_save(instance)}) {'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})


def form_for_fields(field_list): def form_for_fields(field_list):
"Returns a Form class for the given list of Django database field instances." "Returns a Form class for the given list of Django database field instances."
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list]) fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
return type('FormForFields', (BaseForm,), {'fields': fields}) return type('FormForFields', (BaseForm,), {'base_fields': fields})
20 changes: 20 additions & 0 deletions tests/regressiontests/forms/tests.py
Expand Up @@ -2277,6 +2277,8 @@
>>> f.clean_data >>> f.clean_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
# Dynamic construction ########################################################
It's possible to construct a Form dynamically by adding to the self.fields It's possible to construct a Form dynamically by adding to the self.fields
dictionary in __init__(). Don't forget to call Form.__init__() within the dictionary in __init__(). Don't forget to call Form.__init__() within the
subclass' __init__(). subclass' __init__().
Expand All @@ -2292,6 +2294,24 @@
<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> <tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr>
<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr> <tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr>
Instances of a dynamic Form do not persist fields from one Form instance to
the next.
>>> class MyForm(Form):
... def __init__(self, data=None, auto_id=False, field_list=[]):
... Form.__init__(self, data, auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr>
<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr>
>>> field_list = [('field3', CharField()), ('field4', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr>
<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr>
HiddenInput widgets are displayed differently in the as_table(), as_ul() HiddenInput widgets are displayed differently in the as_table(), as_ul()
and as_p() output of a Form -- their verbose names are not displayed, and a and as_p() output of a Form -- their verbose names are not displayed, and a
separate row is not displayed. They're displayed in the last row of the separate row is not displayed. They're displayed in the last row of the
Expand Down

0 comments on commit c93686c

Please sign in to comment.