【发布时间】:2020-11-06 03:31:24
【问题描述】:
我有一个用于将 CSV 文件内容注入数据库的上传表单。到目前为止,我正在努力验证本身。我从基于函数的验证器切换到基于类的验证器,因此我可以将参数传递给验证器本身以使其可重用。
views.py
@login_required
def contact_import(request):
if request.method == 'POST' and request.FILES['file']:
form = ContactImportForm(request.POST,request.FILES)
if form.is_valid():
post_result = form.save(commit=True)
return redirect("/contacts/")
else:
form = ContactImportForm()
return render(request,'contact_import_form.html',{
'form': form
})
forms.py
class ContactImportForm(forms.Form):
headers = ["lastname","firstname","email"]
file = forms.FileField(label='Select your CSV File',validators=[CsvFileValidator(headers)])
def clean_file(self):
file = self.cleaned_data['file']
return file
validators.py
class CsvFileValidator(object):
def __init__(self, headers):
self.headers = headers
def __call__(self,file):
# Extension check
file_extension = os.path.splitext(file.name)[1]
valid_extensions = [ ".csv", ".CSV"]
if not file_extension.lower() in valid_extensions:
msg = "Invalid file, select a valid CSV file"
raise ValidationError("{}".format(msg), status='invalid')
# File content check
try:
csv_format = csv.Sniffer().sniff(file.read(1024))
file.seek(0,0)
except csv.Error:
msg = "Invalid file, select a valid CSV file"
raise ValidationError("{}".format(msg), status='invalid')
csv_reader = csv.reader(file.read().splitlines(), dialect=csv_format,delimiter=";")
for r_index, row in enumerate(csv_reader):
# CSV headers check
if r_index == 0:
if sorted(self.headers) != sorted(row):
msg = "Missing or invalid headers"
raise ValidationError("{}".format(msg), status='invalid')
# Skip blank line
if not "".join(str(field) for field in row):
continue
return True
contact_import_form.html HTML 模板,仅相关部分:
<div class="messages">
{% if form.errors %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
<p class="text-danger"><strong>AN error occured during form validation</strong>
{% for error in form.non_field_errors %}
{{error}}
{% endfor %}
</p>
{% endif %}
{% for field in form %}
{% if field.errors %}
<p class="text-danger"><strong>{{ field.name }} :</strong> {{ field.errors }}</p>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<form id="contact-import-form" method="post" action=" " role="form" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
{{ form.file.label_tag }}
{{ form.file }}
</div>
<div class="form-group">
<label class="col-md-4 control-label" for="submit"></label>
<div class="col-md-8">
<button id="submit" name="submit" class="btn btn-primary" value="Submit">Valider</button>
<a href="${%url 'contact_list' %}" id="cancel" name="cancel" class="btn btn-default">Annuler</a>
</div>
</div>
</form>
现在提交表单后出现错误:
TypeError: cannot use a string pattern on a bytes-like object
这个错误指向我的验证器中的文件内容检查,特别是上面的行:
csv_format = csv.Sniffer().sniff(file.read(1024))
如何实现这个自定义的基于类的验证器才能正常工作?另外我想更新数据库的缺失代码应该存在于if form.is_valid():代码块的视图中。
如何使用基于类的验证器即时验证 CSV 文件上传,然后使用其数据填充模型?
【问题讨论】:
-
当传递给 CsvFileValidator 时,'file' 类型为 "'django.core.files.uploadedfile.InMemoryUploadedFile"。在 io.TextIOWrapper 中包装文件似乎没问题。但是,验证器引发的异常仍然没有出现在我的模板中。
-
表单验证按预期工作,但在 form.errors 或 form.non_field_errors 中没有出现异常消息。如何在其中提供验证异常消息?
-
看来是验证器本身的问题,但我不知道如何正确解决问题。如果没有验证器,如果表单有效,则可以读取文件。
标签: django csv django-forms