Валидация загруженных файлов в 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