Валидация загруженных файлов в Django приводит к сообщению "InMemoryUploadedFile не поддерживается"

У меня есть шаблон, в котором пользователь может выбрать и загрузить файл со своего компьютера. В конечном итоге файл загружается на S3, но сначала он проверяется с помощью пользовательской валидации для проверки некоторого содержимого файла. Для проверки содержимого скрипт считывает строки файла в forms.py:

from io import TextIOWrapper

class UploadForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('myfilefield',)

    def clean_myfilefield(self):
        file = self.cleaned_data['myfilefield']
        read_file = TextIOWrapper(self.cleaned_data.get('myfilefield'), encoding='ASCII')
        headerCounter = 0
        for line in read_file:
            if ">" in line:
                headerCounter += 1
        if headerCounter > 1:
            error = "Custom error message 1"
            raise ValidationError(error)
        if headerCounter == 0:
            error = "Custom error message 2"
            raise ValidationError(error)
        return file

Сообщения о проверке отображаются правильно, но если файл проходит проверку, я получаю следующее сообщение об ошибке (предполагается, что myfile.txt - это загруженный файл):

Ввод myfile.txt типа: <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> не поддерживается.

Валидация и загрузка работают нормально, если я убираю часть TextIOWrapper и проверки ошибок. Так что я предполагаю, что есть что-то в чтении файла, что преобразует его в InMemoryUploadedFile, который не совместим с загрузкой файла? Я пытался найти решения для "преобразования" InMemoryUploadedFile обратно в "обычный" (?) файл, но ничего не помогло.

Другой соответствующий код:

views.py

@login_required
def detail_view(request, id):
    current_object = MyModel.objects.get(id=id)
    context = {}
    context["data"] = current_object

    if request.method == 'POST':
        form = UploadForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return render(request, 'this_page.html', context)
        else:
            return render(request, 'this_page.html', {"form" : form})
    else:
        context["form"] = UploadForm()
        return render(request, 'this_page.html', context)

models.py (только релевантный материал)

class MyModel(models.Model):
    myfilefield = models.FileField(upload_to="media/stuff", blank=True, null=True)

Используется Django 3.2

Вы должны предоставить file-object для TextIOWrapper.

InMemoryUploadedFile - это обертка вокруг объекта файла. Вы можете получить доступ к объекту файла, используя атрибут file.

from io import TextIOWrapper

class UploadForm(forms.ModelForm):
    ...

    def clean_myfilefield(self):
        my_file = self.cleaned_data['myfilefield']
                                   HERE ⬇️
        read_file = TextIOWrapper(my_file.file, encoding='ASCII')

        ...

РЕШЕНО! Мне нужно было закрыть поток TextIOWrapper, добавив read_file.detach() в forms.py.

from io import TextIOWrapper

class UploadForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('myfilefield',)

    def clean_myfilefield(self):
        file = self.cleaned_data['myfilefield']
        read_file = TextIOWrapper(self.cleaned_data.get('myfilefield'), encoding='ASCII')
        headerCounter = 0
        for line in read_file:
            if ">" in line:
                headerCounter += 1
        if headerCounter > 1:
            error = "Custom error message 1"
            raise ValidationError(error)
        if headerCounter == 0:
            error = "Custom error message 2"
            raise ValidationError(error)
        read_file.detach()
        return file
Вернуться на верх