【发布时间】:2021-08-13 14:04:57
【问题描述】:
还有一个关于风格和良好做法的问题。 我将展示的代码可以工作并执行功能。但我想知道它是否可以作为解决方案,或者它可能太丑陋了?
由于问题有点晦涩,我在最后给出几点。
所以,用例。
我有一个包含这些项目的网站。有一个功能可以按用户添加项目。现在我想要一个通过 csv 文件添加多个项目的功能。
它应该如何工作?
- 用户转到特殊上传页面。
- 用户选择一个csv文件,点击上传。
- 然后他被重定向到显示 csv 文件内容的页面(作为表格)。
- 如果用户没问题,他点击“是”(带有“confirm_items_upload”值的按钮)并将文件中的项目添加到数据库中(如果它们没问题)。
我已经看到了 django 批量上传的示例,它们看起来很清楚。但是我没有找到带有中间“验证-确认”页面的示例。 那我是怎么做到的:
- 在 views.py 中:上传 csv 文件页面的视图
def upload_item_csv_file(request):
if request.method == 'POST':
form = UploadItemCsvFileForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file_name = handle_uploaded_item_csv_file(request.FILES['item_csv_file'])
request.session['uploaded_file'] = uploaded_file_name
return redirect('show_upload_csv_item')
else:
form = UploadItemCsvFileForm()
return render(request, 'myapp/item_csv_upload.html', {'form': form})
- 在 utils.py 中:handle_uploaded_item_csv_file - 只需保存文件并返回文件名
def handle_uploaded_item_csv_file(f):
now = datetime.now()
# YY_mm_dd_HH_MM
dt_string = now.strftime("%Y_%m_%d_%H_%M")
file_name = os.path.join(settings.MEDIA_ROOT, f"tmp_csv/item_csv_{dt_string}.csv")
with open(file_name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
return f"tmp_csv/item_csv_{dt_string}.csv"
- 在 views.py 中:show_upload_csv_item 的视图
@transaction.atomic
def show_uploaded_file(request):
if 'uploaded_file' in request.session :
file_name = request.session['uploaded_file']
else :
print("Something wrong : raise 404")
raise Http404
if not os.path.isfile(os.path.join(settings.MEDIA_ROOT, file_name)):
print("Something wrong, file does not exist : raise 404")
raise Http404
with open(os.path.join(settings.MEDIA_ROOT, file_name)) as csvfile :
fieldnames = ['serial_number', 'type', 'shipping_date', 'comments']
csv_reader = csv.DictReader(csvfile, delimiter=';', fieldnames=fieldnames)
list_items = list(csv_reader)
if request.POST and ("confirm_items_upload" in request.POST) :
if request.POST["confirm_items_upload"] == "yes" :
for cur_item in list_items :
if not cur_item['shipping_date'] :
cur_item.pop('shipping_date', None)
try :
Item.objects.create(**cur_item)
except IntegrityError :
messages.warning(request, f"This Item : {cur_item} - already exists. No items were added." )
os.remove(os.path.join(settings.MEDIA_ROOT, file_name))
return redirect('items')
else :
return render(request, 'myapp/item_csv_uploaded.html', {'items': list_items})
- 在 forms.py 中:表格非常明显,但要清楚
class UploadItemCsvFileForm(forms.Form):
item_csv_file = forms.FileField()
这是问题/要点。
a) 即使显然它可以更好,这个解决方案是可以接受还是根本不可以接受?
b) 我使用 "request.session" 将 'uploaded_file' 从一个视图传递到另一个视图,这是一个好习惯吗?不使用 GET 变量还有其他方法吗?
c) 起初我的愿望是避免保存 csv 文件。但我不知道该怎么做? 将所有文件读取到 request.session 对我来说似乎不是一个好主意。有没有可能在 Django 中将文件上传到内存中?
d) 如果我必须使用 tmp 文件。如果用户中途放弃上传,我应该如何处理(例如,他看到了确认页面,但没有点击“是”并决定重写他的文件)。如何删除 tmp 文件?
e) 附加的小问题:Django 中对上传文件进行了哪些检查?例如,我如何检查该文件是否至少是一个文本文件?我应该这样做吗?
也欢迎所有其他评论。
【问题讨论】:
标签: python django django-views bulk