I've borrowed Alex Gaynor's code for building MultipleForm factories
[0] and updated it to work with optional form fields.

The idea comes from the following situation: you have a profile page
with many services (eg. blip, twitter, etc) that you link to via
ForeignKey.

class MicroBlog(models.Model):
    service = models.IntegerField(choices=SOCIAL_MEDIAS)
    nick = models.CharField(max_length=50)
    # other stuff (eg. verification, update_date, etc)

You would like to build the profile page quickly - so you use a
ModelForm and the MultipleForm factory.

class MicroBlogForm(forms.ModelForm):
    class Meta:
        model = MicroBlog

BigProfileForm = multiple_form_factory({
        'twitter': SocialMicroBlogForm,
        'flaker': SocialMicroBlogForm,
        # other kinds of forms (so can't really use querysets.. I
think. I could be wrong!)
        }, ['twitter', 'flaker'])

The only problem with this is that all submitted forms should validate
BUT not all forms are required. For example, we can't expect everyone
to have a twitter account. One way is to make the fields optional (but
then we need to validate outside of forms), so the only real way to do
it is to mark optional forms.

I've modified Alex's code to support optional fields:

BigProfileForm = multiple_form_factory({
        'twitter': SocialMicroBlogForm,
        'flaker': SocialMicroBlogForm,
        }, ['twitter', 'flaker']
          , ['twitter'])                 # twitter is optional, flaker
is required

The modified code is posted below. If possible, I'd appreciate some
comments if this is the right way to go about it. Also, there's one
bug I can't figure out - if an empty form is submitted (without any
data), the rendered form shows required field errors on all forms
(even optional). The correct handling is to only show required field
errors on optional fields that had some data submitted. Could someone
point out where my problem is?

Code:

from django.forms.util import ErrorList
from django.utils.datastructures import SortedDict
from django.utils.safestring import mark_safe

def multiple_form_factory(form_classes, form_order=None,
optional_forms=[]):
   if form_order:
       form_classes = SortedDict([(prefix, form_classes[prefix])
                                  for prefix in form_order])
   else:
       form_classes = SortedDict(form_classes)
   return type('MultipleForm', (MultipleFormBase,),
               {'form_classes': form_classes,
                'optional_forms': optional_forms})

class MultipleFormBase(object):
   def __init__(self, data=None, files=None, auto_id='id_%s',
                prefix=None, initial=None, error_class=ErrorList,
                label_suffix=':', empty_permitted=False):
       initial = initial or {}
       if prefix is None:
           prefix = ''
       self.forms = []
       self.extra_forms_info = []
       for i, (form_prefix, form_class) in enumerate
(self.form_classes.iteritems()):
           self.extra_forms_info.append({
               'prefix': form_prefix,
               'optional': form_prefix in self.optional_forms,
               })
           self.forms.append(form_class(data, files, auto_id, prefix +
form_prefix,
                                       initial.get(i, None),
                                       error_class, label_suffix,
empty_permitted))

   def __unicode__(self):
       return self.as_table()

   def __iter__(self):
       for form in self.forms:
           for field in form:
               yield field

   def non_optional_forms(self):
       for i,form in enumerate(self.forms):
           if self.extra_forms_info[i]['optional'] and not
form._get_changed_data():
               continue
           yield form

   def is_valid(self):
       # raise Exception, list(self.non_optional_forms())
       return all(form.is_valid() for form in self.non_optional_forms
())

   def as_table(self):
       return mark_safe('\n'.join(form.as_table() for form in
self.forms))

   def as_ul(self):
       return mark_safe('\n'.join(form.as_ul() for form in
self.forms))

   def as_p(self):
       return mark_safe('\n'.join(form.as_p() for form in self.forms))

   def is_multipart(self):
       return any(form.is_multipart() for form in self.forms)

   def save(self, commit=True):
       return tuple(form.save(commit) for form in
self.non_optional_forms())
   save.alters_data = True


Thanks in advance,
Norbert Wójtowicz

[0] http://en.av5.info/lazypython/2009/05/eurodjangocon-2009.html

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to