class StrikingVesselInfoForm(VesselInfoForm): '''\ To be used with a ContactForm on the same page for adding a new captain Contact. ''' contact_choices = ( ('new', 'add a new contact'), ('reporter', 'use the same contact as the reporter'), ('observer', 'use the same contact as the observer'), ('other', 'use an existing contact'), ('none', 'no contact info'), ) contact_choice = forms.ChoiceField( choices= contact_choices, initial= 'none', widget= forms.RadioSelect, #help_text= "create a new contact for the vessel's contact?", ) captain_choices = ( ('new', 'add a new contact'), ('reporter', 'use the same contact as the reporter'), ('observer', 'use the same contact as the observer'), ('vessel', 'use the same contact as for the vessel'), ('other', 'use an existing contact'), ('none', 'no contact info'), ) captain_choice = forms.ChoiceField( choices= captain_choices, initial= 'none', widget= forms.RadioSelect, #help_text= "create a new contact for the vessel's captain?", ) # should be the same as whatever ModelForm would generate for the 'captain' # field, except it's not required. # TODO why not just have it generate field for captain? (which wouldn't # be required anyway) _f = StrikingVesselInfo._meta.get_field('captain') existing_captain = forms.ModelChoiceField( queryset= Contact.objects.all(), required= False, help_text= _f.help_text, label= _f.verbose_name.capitalize(), ) def __init__(self, data=None, initial=None, instance=None, prefix=None, *args, **kwargs): # the values for captain_choice and existing_captain can be set from # a passed 'instance', but such values should be overrideable by the # passed 'initial' argument if not instance is None: if initial is None: initial = {} if not 'captain_choice' in initial: if instance.captain == instance.contact and not instance.contact is None: initial['captain_choice'] = 'vessel' elif instance.captain is not None: initial['captain_choice'] = 'other' else: initial['captain_choice'] = 'none' if not 'existing_captain' in initial: if not instance.captain is None: initial['existing_captain'] = instance.captain.id super(StrikingVesselInfoForm, self).__init__(data, initial=initial, instance=instance, prefix=prefix, *args, **kwargs) # the ContactForm for new captain contacts new_captain_prefix = 'new_captain' if not prefix is None: new_captain_prefix = prefix + '-' + new_captain_prefix self.new_captain = ContactForm(data, prefix=new_captain_prefix) # TODO for: # # __unicode__ # __iter__ # as_table # as_ul # as_p # is_multipart # hidden_fields # visible_fields # # should we output the corresponding results from self.new_captain as well? def is_valid(self): valid = super(StrikingVesselInfoForm, self).is_valid() # calling is_valid will # access self.error, which will # call self.full_clean, which will # populate self.cleaned_data if not bool(self._errors) if not self.errors and hasattr(self, 'cleaned_data'): if self.cleaned_data['captain_choice'] == 'new': valid = self.new_captain.is_valid() return valid @property def errors(self): errors = super(StrikingVesselInfoForm, self).errors # accessing self.errors, will # call self.full_clean, which will # populate self.cleaned_data if not bool(self._errors) if not errors and hasattr(self, 'cleaned_data'): if self.cleaned_data['captain_choice'] == 'new': new_captain_errors = self.new_captain.errors if new_captain_errors: errors['new_captain'] = new_captain_errors return errors def full_clean(self): super(StrikingVesselInfoForm, self).full_clean() self.new_captain.full_clean() # note that we don't need to override has_changed to handle self.new_contact def save(self, commit=True): svi = super(StrikingVesselInfoForm, self).save(commit=False) if self.cleaned_data['captain_choice'] == 'new': nc = self.new_captain.save(commit=commit) if commit: svi.captain = nc else: old_m2m = self.save_m2m def new_m2m(self): old_m2m() svi.captain = nc svi.save() self.save_m2m = new_m2m if self.cleaned_data['captain_choice'] == 'vessel': svi.captain = svi.contact if self.cleaned_data['captain_choice'] == 'other': svi.captain = self.cleaned_data['existing_captain'] if self.cleaned_data['captain_choice'] == 'none': svi.captain = None if commit: svi.save() self.save_m2m() return svi class Meta: model = StrikingVesselInfo exclude = ('contact', 'captain')
class VesselInfoForm(forms.ModelForm): # ModelForm won't fill in all the handy args for us if we sepcify our own # field _f = VesselInfo._meta.get_field('flag') flag = FlagField( required= _f.blank != True, help_text= _f.help_text, label= _f.verbose_name.capitalize(), initial='US', ) contact_choices = ( ('new', 'add a new contact'), ('reporter', 'use the same contact as the reporter'), ('observer', 'use the same contact as the observer'), ('other', 'use an existing contact'), ('none', 'no contact info'), ) contact_choice = forms.ChoiceField( choices= tuple(), initial= 'none', widget= forms.RadioSelect, #help_text= "create a new contact for the vessel's contact?", ) # should be the same as whatever ModelForm would generate for the 'contact' # field, except with a different name _f = VesselInfo._meta.get_field('contact') existing_contact = forms.ModelChoiceField( queryset= Contact.objects.all(), required= False, help_text= _f.help_text, label= _f.verbose_name.capitalize(), ) # TODO how to retain positional args without copying them from super? def __init__(self, data=None, initial=None, instance=None, prefix=None, *args, **kwargs): # the values for contact_choice and existing_contact can be set from # a passed 'instance', but such values should be overrideable by the # passed 'initial' argument if not instance is None: if initial is None: initial = {} if not 'contact_choice' in initial: initial['contact_choice'] = 'other' if not instance.contact is None else 'none' if not 'existing_contact' in initial: if not instance.contact is None: initial['existing_contact'] = instance.contact.id super(VesselInfoForm, self).__init__(data, initial=initial, instance=instance, prefix=prefix, *args, **kwargs) # make contact_choices overrideable self['contact_choice'].field.choices = self.contact_choices # the ContactForm for new contacts new_contact_prefix = 'new_contact' if not prefix is None: new_contact_prefix = prefix + '-' + new_contact_prefix self.new_contact = ContactForm(data, prefix=new_contact_prefix) # TODO for: # # __unicode__ # __iter__ # as_table # as_ul # as_p # is_multipart # hidden_fields # visible_fields # # should we output the corresponding results from self.new_contact as well? def is_valid(self): valid = super(VesselInfoForm, self).is_valid() # calling is_valid will # access self.errors, which will # call self.full_clean, which will # populate self.cleaned_data or self._errors if not self.errors and hasattr(self, 'cleaned_data'): if self.cleaned_data['contact_choice'] == 'new': valid = self.new_contact.is_valid() return valid @property def errors(self): errors = super(VesselInfoForm, self).errors if not errors and hasattr(self, 'cleaned_data'): if self.cleaned_data['contact_choice'] == 'new': new_contact_errors = self.new_contact.errors if new_contact_errors: errors['new_contact'] = new_contact_errors return errors def full_clean(self): super(VesselInfoForm, self).full_clean() self.new_contact.full_clean() # note that we don't need to override has_changed to handle self.new_contact def save(self, commit=True): vi = super(VesselInfoForm, self).save(commit=False) if self.cleaned_data['contact_choice'] == 'new': nc = self.new_contact.save(commit=commit) if commit: vi.contact = nc else: old_m2m = self.save_m2m def new_m2m(self): old_m2m() vi.contact = nc vi.save() self.save_m2m = new_m2m if self.cleaned_data['contact_choice'] == 'other': vi.contact = self.cleaned_data['existing_contact'] if self.cleaned_data['contact_choice'] == 'none': vi.contact = None if commit: vi.save() self.save_m2m() return vi class Meta: model = VesselInfo # existing_contact is used instead # TODO why? exclude = ('contact')