Strangely the validator class (file is processed differently) doesn't
throw an "I/O operation on closed file" exception when file is converted
with io.TextIOWrapper and processed (see validators.py code below) and
work properly.
It seems that somehow file (in memory) is flushed right after being posted
without any mean to process it further with form.is_valid().
Tested on Django 2.2.25 and 3.0.8.
**forms.py**
{{{
from .validators import CsvFileValidator
class FileImportForm(forms.Form):
headers =
["lastname","firstname","gender","title","entity","email","company","address","phone"]
file = forms.FileField(label='CSV
file',validators=[CsvFileValidator(headers)])
def clean_file(self):
file = self.cleaned_data['file']
return file
}}}
**views.py**
{{{
@login_required
def file_import(request):
if request.method == 'POST':
form = FileImportForm(request.POST,request.FILES)
if form.is_valid():
if request.FILES['file']:
file_post = request.FILES['file']
# Offending line below (I/O operation on closed file)
file_content = file_post.read().decode('UTF-8')
return redirect("/foo")
else:
form = FileImportForm()
return render(request,"file_import.html", { 'form': form })
}}}
**validators.py**
{{{
import csv
class CsvFileValidator(object):
def __init__(self, headers):
self.headers = headers
def __call__(self,file):
file_extension = os.path.splitext(file.name)[1]
valid_extensions = [ ".csv", ".CSV"]
if not file_extension.lower() in valid_extensions:
msg = "Invalid file extension"
logger.error(msg)
raise ValidationError("{}".format(msg), code='invalid')
try:
csv_file = io.TextIOWrapper(file)
csv_format = csv.Sniffer().sniff(csv_file.read(1024))
csv_file.seek(0,0)
except csv.Error:
msg = "Invalid CSV file"
logger.error(msg)
logger.debug("Exception msg : {}".format(msg))
raise ValidationError("{}".format(msg), code='invalid')
csv_reader = csv.reader(csv_file.read().splitlines(),
dialect=csv_format,delimiter=";")
for r_index, row in enumerate(csv_reader):
if r_index == 0:
if sorted(self.headers) != sorted(row):
msg = "Invalid or missing CSV headers"
logger.error(msg)
raise ValidationError("{}".format(msg),
code='invalid')
if not "".join(str(field) for field in row):
continue
msg = "Valid CSV file"
logger.debug(msg)
return True
def __eq__(self,other):
return (
isinstance(other, self.__class__)
and self.headers == other.headers
)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31833>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* owner: nobody => Harpreet Sharma
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/31833#comment:1>
--
Ticket URL: <https://code.djangoproject.com/ticket/31833#comment:2>
* status: assigned => closed
* resolution: => invalid
Comment:
Please don't use trac as a support channel. Everything works properly with
a stub `CsvFileValidator`:
{{{
class CsvFileValidator:
def __call__(self,file):
return True
}}}
so it's not an issue in Django but in your implementation.
Closing per TicketClosingReasons/UseSupportChannels.
--
Ticket URL: <https://code.djangoproject.com/ticket/31833#comment:3>