| | 120 | |
| | 121 | === Update 15.03.2006 === |
| | 122 | |
| | 123 | After 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: |
| | 124 | |
| | 125 | {{{ |
| | 126 | #!python |
| | 127 | class ApplicationAddManipulator(applications.AddManipulator): |
| | 128 | |
| | 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 |
| | 137 | |
| | 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'") |
| | 152 | |
| | 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 applications.AddManipulator.save(self, new_data) |
| | 164 | }}} |
| | 165 | |
| | 166 | Here'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. |
| | 167 | |
| | 168 | Why is this better? Validation is only done where appropriate - in the 'isValidPortList' function and nowhere else. |
| | 169 | |
| | 170 | Are 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. |