pythondjangofile-uploaddjango-class-based-viewsdjango-file-upload

Reopening a closed django InMemoryFileUpload using class based views


I have a Django project which involves a user uploading a CSV file via a form. I parse this file in the forms clean method, and then in the views form_valid method I want to read the file data again (for the purposes of long term storage).

My issue is that after parsing the file in the clean method, I'm no longer able to perform IO operations on the file object, any attempt to do so raises an error. Code as below:

class MyForm(forms.Form):
    file = forms.FileField()
    def clean(self):
        cleaned_data = super().clean()
        file = cleaned_data["file"]
        reader = csv.DictReader(io.TextIOWrapper(file))
        for row in reader:
            ...  # process data
        return cleaned_data

class MyView(generic.FormView):
    form_class = MyForm

    def form_valid(self, form):
        file = form.files["file"]
        file.read()  # raises ValueError: I/O operation on closed file.

At this point it's no longer possible to call other methods like file.open() either as this leads to the same exception being raised.

What I am finding confusing about this is that there are other examples in my application of where IO operations can be performed on the file in the form_valid method, example below:

class MyOtherForm(forms.Form):
    file = forms.FileField()

class MyOtherView(generic.FormView):
    form_class = MyOtherForm

    def form_valid(self, form):
        file = form.files["file"]
        file.read()  # this works, no error raised.

My interpretation of this is that somehow the process of reading the file is what triggers it to be closed, although I am not sure where or how. What I want to know is if there is any way I can prevent the file from closing after I read it, or reopen the file after it has closed?

I realise I could perform both the initial read/parse and the second one inside the same method and this should solve my issue. However, at that point I'm starting to tangle my concerns so would prefer to avoid it.


Solution

  • It turns out the problem was the use of the io.TextIOWrapper, it was resolved by calling the detach method on the text wrapper before it was cleaned up.

    A more detailed explanation is covered in this other SO post: Why is TextIOWrapper closing the given BytesIO stream?