I have a Django form with a lot of fields, including some FileFields. I want to validate the form based on the contents of the files: they must be valid gdal datasets, and if both are present (one of them is optional) then the optional dataset's extent must entirely cover the required file's extent.
To do this, I have to save the files to a temporary place and process them there, which is different from how I'd usually treat uploaded files in Django (only process them after form validation). The view needs to know that the files are already saved and how to get at them, and it has to make sure to clean up the files afterwards.
I've omitted parts of the code, the relevant parts of the Form look like this:
class FormStep1(forms.Form): waterlevel = forms.FileField(required=True) customlanduse = forms.FileField(required=False) def add_field_error(self, field, message): """Assumes this field has no errors yet""" self._errors[field] = self.error_class([message]) def clean(self): """Check that optional custom datasets cover the right area.""" cleaned_data = super(FormStep1, self).clean() if 'customlanduse' and 'waterlevel' in cleaned_data: # Save files to a temporary directory to check them self.temp_directory = tempfile.mkdtemp() for field in ('customlanduse', 'waterlevel'): filename = os.path.join( self.temp_directory, cleaned_data[field].name) with open(filename, 'wb') as f: for chunk in cleaned_data[field].chunks(): f.write(chunk) cleaned_data[field + '_filename'] = filename # Open datasets customlanduse = gdal.Open(cleaned_data['customlanduse_filename']) waterlevel = gdal.Open(cleaned_data['waterlevel_filename']) if customlanduse is None: self.add_field_error("customlanduse", "Can't open dataset") if waterlevel is None: self.add_field_error("waterlevel", "Can't open dataset") if customlanduse is not None and waterlevel is not None: water_extent = extent_from_dataset(waterlevel) landuse_extent = extent_from_dataset(customlanduse) if not extent_within_extent( inner_extent=water_extent, outer_extent=landuse_extent): self.add_field_error('customlanduse', "Not within extent") return cleaned_data def cleanup_files(self): """This must be called after processing the saved files.""" shutil.rmtree(self.temp_directory)
In short: don't really like handling downloaded files in a Form, but it works. Is there a better way?