Pau*_*rov 6 python django bulk django-views
还有一个关于风格和良好实践的问题。我将展示的代码可以工作并执行功能。但我想知道它是否可以作为解决方案,或者它可能太丑陋了?
由于问题有点晦涩难懂,我最后再给出一些观点。
那么,用例。
我有一个包含这些物品的网站。有一个按用户添加项目的功能。现在我想要一个通过 csv 文件添加多个项目的功能。
应该如何运作?
我已经看到了 django 批量上传的例子,它们看起来很清楚。但我没有找到带有中间“验证确认”页面的示例。那么我是怎么做到的:
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})
Run Code Online (Sandbox Code Playgroud)
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"
Run Code Online (Sandbox Code Playgroud)
@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})
Run Code Online (Sandbox Code Playgroud)
class UploadItemCsvFileForm(forms.Form):
item_csv_file = forms.FileField()
Run Code Online (Sandbox Code Playgroud)
这是问题/要点。
a) 即使显然它可以更好,这个解决方案是否可以接受或根本不可以接受?
b)我使用“request.session”将“uploaded_file”从一个视图传递到另一个视图,这是一种好的做法吗?有没有另一种方法可以不使用 GET 变量来做到这一点?
c) 起初我的愿望是避免保存 csv 文件。但我不知道该怎么做?将所有文件读取到 request.session 对我来说似乎不是一个好主意。是否有可能将文件上传到 Django 内存中?
d) 如果我必须使用 tmp 文件。如果用户在中间放弃上传(例如,他看到确认页面,但没有单击“是”并决定重写他的文件),我应该如何处理这种情况。如何删除 tmp 文件?
e) 附加小问题:Django 对上传的文件进行了哪些检查?例如,我如何检查该文件至少是一个文本文件?我应该这样做吗?
也欢迎所有其他评论。
a) 即使显然它可以更好,这个解决方案是否可以接受或根本不可以接受?
我认为它有一些您想要解决的问题,但是使用文件系统并仅存储文件名的一般想法是可以接受的,具体取决于您需要服务的用户数量以及您想要进行的数据一致性和并发访问的保证。
我会考虑上传的文件临时数据可能会因系统故障而丢失。如果您想提供不丢失数据的任何保证,您希望将其存储在数据库中而不是文件系统中。
b)我使用“request.session”将“uploaded_file”从一个视图传递到另一个视图,这是一种好的做法吗?有没有另一种方法可以不使用 GET 变量来做到这一点?
使用 request.session 有优点也有缺点。
最后一点与剩余文件问题相关:如果您丢失了有关仍需要哪些文件的信息,则清理工作会变得更加困难(尽管理论上,您可以从会话存储中检索仍需要哪些文件)。
如果由于用户清除其 cookie 或更改设备而导致会话可能结束或更改,您可以考虑将文件名添加到UserProfile数据库中。这样,它就不受会话的约束。
c) 起初我的愿望是避免保存 csv 文件。但我不知道该怎么做?将所有文件读取到 request.session 对我来说似乎不是一个好主意。是否有可能将文件上传到 Django 内存中?
你想存储状态。存储状态的首选方法是数据库或会话存储。您可以加载整个 CSVFile 并将其作为文本放入数据库中。这是否可以接受取决于您的数据库处理大型非结构化数据的能力。传统数据库最初并不是为此构建的,但是现在它们中的大多数都可以很好地处理小型二进制文件。数据库可以为您提供诸如 ACID 保证之类的优势,其中对文件系统上同一文件的并发写入可能会破坏该文件。请参阅dba stackexchange 上的讨论
您的数据库可能有关于该主题的文档,例如,有一个关于 postgres 中的二进制数据的页面。
d) 如果我必须使用 tmp 文件。如果用户在中间放弃上传(例如,他看到确认页面,但没有单击“是”并决定重写他的文件),我应该如何处理这种情况。如何删除 tmp 文件?
一些想法:
您可能希望将服务器限制为每个用户存储一个文件,以便攻击者无法填充您的文件系统。
e) 附加小问题:Django 对上传的文件进行了哪些检查?例如,我如何检查该文件至少是一个文本文件?我应该这样做吗?
您肯定想为文件设置一些最大文件大小,如此处所述。您可以限制允许的文件扩展名,但这只是一个可用性问题。攻击者还可以向您提供任何可接受的扩展名的垃圾数据。
请记住:如果您仅将 csv 存储为每次访问特定视图时加载和解析的文本数据,那么这可能是攻击者耗尽您的服务器的一种简单方法,从而使他们能够轻松进行 DoS 攻击。
总的来说,这取决于您想要做出什么保证、您拥有多少用户以及他们的可信度。如果用户可能是恶意的,您需要牢记所有可能类型的数据提取和资源耗尽攻击。文件系统不会横向扩展(至少不像数据库那么容易)。
我知道在一个项目中有类似的设置,其中只允许少数特权用户上传内容,并且我们可以容忍在失败时删除所有临时文件。用户只需重新上传他们的文件。这很好用。
| 归档时间: |
|
| 查看次数: |
2365 次 |
| 最近记录: |