Implemented subclassing Forms in newforms

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4506 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-02-14 23:44:46 +00:00
parent 4c4209b144
commit 0518205308
3 changed files with 93 additions and 1 deletions

View File

@ -31,10 +31,21 @@ class SortedDictFromList(SortedDict):
return SortedDictFromList([(k, copy.copy(v)) for k, v in self.items()])
class DeclarativeFieldsMetaclass(type):
"Metaclass that converts Field attributes to a dictionary called 'base_fields'."
"""
Metaclass that converts Field attributes to a dictionary called
'base_fields', taking into account parent class 'base_fields' as well.
"""
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.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
# If this class is subclassing another Form, add that Form's fields.
# Note that we loop over the bases in *reverse*. This is necessary in
# order to preserve the correct order of fields.
for base in bases[::-1]:
if hasattr(base, 'base_fields'):
fields = base.base_fields.items() + fields
attrs['base_fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs)

View File

@ -571,6 +571,46 @@ is a list-like object that is displayed as an HTML ``<ul>`` when printed::
>>> str(f['subject'].errors)
''
Subclassing forms
-----------------
If you subclass a custom ``Form`` class, the resulting ``Form`` class will
include all fields of the parent class(es), followed by the fields you define
in the subclass.
In this example, ``ContactFormWithPriority`` contains all the fields from
``ContactForm``, plus an additional field, ``priority``. The ``ContactForm``
fields are ordered first::
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False)
>>> print f.as_ul()
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li>
It's possible to subclass multiple forms, treating forms as "mix-ins." In this
example, ``BeatleForm`` subclasses both ``PersonForm`` and ``InstrumentForm``
(in that order), and its field list includes the fields from the parent
classes::
>>> class PersonForm(Form):
... first_name = CharField()
... last_name = CharField()
>>> class InstrumentForm(Form):
... instrument = CharField()
>>> class BeatleForm(PersonForm, InstrumentForm):
... haircut_type = CharField()
>>> b = Beatle(auto_id=False)
>>> print b.as_ul()
<li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li>
<li>Instrument: <input type="text" name="instrument" /></li>
<li>Haircut type: <input type="text" name="haircut_type" /></li>
Fields
======

View File

@ -2682,6 +2682,47 @@ purposes, though.
<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li>
# Subclassing forms ###########################################################
You can subclass a Form to add fields. The resulting form subclass will have
all of the fields of the parent Form, plus whichever fields you define in the
subclass.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
>>> class Musician(Person):
... instrument = CharField()
>>> p = Person(auto_id=False)
>>> print p.as_ul()
<li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" /></li>
>>> m = Musician(auto_id=False)
>>> print m.as_ul()
<li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" /></li>
<li>Instrument: <input type="text" name="instrument" /></li>
Yes, you can subclass multiple forms. The fields are added in the order in
which the parent classes are listed.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
>>> class Instrument(Form):
... instrument = CharField()
>>> class Beatle(Person, Instrument):
... haircut_type = CharField()
>>> b = Beatle(auto_id=False)
>>> print b.as_ul()
<li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" /></li>
<li>Instrument: <input type="text" name="instrument" /></li>
<li>Haircut type: <input type="text" name="haircut_type" /></li>
# Forms with prefixes #########################################################
Sometimes it's necessary to have multiple forms display on the same HTML page,