Changes between Version 5 and Version 6 of CustomFormFields

03/15/2006 09:07:55 AM (9 years ago)

Update explaining why custom manipulators are a better option


  • CustomFormFields

    v5 v6  
    1111class Port(meta.model):
    1212    number = meta.IntegerField()
    4950class FormPortField(formfields.TextField):
    118119Wow.  What a mess.  We end up validating the data three times over.  What a pain.  But it works.
     121=== Update 15.03.2006 ===
     123After much searching and fiddling and wondering I've decided that I went about this all wrong.  Custom form fields are not the answer to my problem, manipulators are.  This is the code I'm now using:
     127class ApplicationAddManipulator(applications.AddManipulator):
     129    def __init__(self):
     130        applications.AddManipulator.__init__(self)
     131        newfields = []
     132        for field in self.fields:
     133            if field.field_name == 'ports':
     134                field = formfields.TextField(field_name='ports', length=30, maxlength=200, validator_list=[self.isValidPortList])
     135            newfields.append(field)
     136        self.fields = newfields
     138    def isValidPortList(self, field_data, all_data):
     139        for port in field_data.split(','):
     140            if port.lower().strip() != 'icmp':
     141                try:
     142                    no, proto = port.split(':')
     143                except ValueError:
     144                    raise validators.ValidationError, _("Ports should be specified in the form 'number:protocol'.  E.g. '80:tcp'")
     145                try:
     146                    if int(no) < 1 or int(no) > 65355:
     147                        raise validators.ValidationError, _("Port number must be between 1 and 65355")
     148                except ValueError:
     149                    raise validators.ValidationError, _("Port number must be between 1 and 65355")
     150                if proto.lower().strip() not in ['tcp', 'udp']:
     151                    raise validators.ValidationError, _("Protocol must be either 'TCP' or 'UDP'")
     153    def save(self, new_data):
     154        portIds = []
     155        if new_data['ports'] and new_data['ports'].strip().lower():
     156            for port in new_data['ports'].split(','):
     157                if port.strip().lower() == 'icmp':
     158                    portIds.append(ports.get_object(protocol__exact='icmp').id)
     159                else:
     160                    no, proto = port.strip().lower().split(':')
     161                    portIds.append(ports.get_object(protocol__exact=proto, number__exact=int(no)).id)
     162            new_data['ports'] = portIds
     163        return, new_data)
     166Here's what the code actually does.  The new custom manipulator subclasses the standard AddManipulator for the Application model.  When a new instance is created (i.e. the __init__ method is called) then the AddManipulator's fields are copied and the 'ports' multiple-select box replaced with a standard text field.  The field has a custom validator attached to it and the save method is overridden to convert the user's input into a valid list of ports.
     168Why is this better?  Validation is only done where appropriate - in the 'isValidPortList' function and nowhere else.
     170Are there still issues?  Yep.  I still don't like the calling of 'do_html2python' when data could be invalid.  This really doesn't appear to make sense to me.  A lot of the manipulator code will change when Magic Removal hits trunk.  To what extent this will correct my concerns remains to be seen.
Back to Top