是否可以使用 FileFields 预填充 Django FormSets?
Is it possible to prepopulate Django FormSets with FileFields?
感谢您停下来花时间阅读我的问题。这是我的问题。我正在表单上创建附件。多种的。一切都很好。这就是问题所在……我想在更新表单上“获取”这些附件,以便显示它们,并且如果表单获得批准则可以将其删除。这证明具有挑战性。在某些情况下,我通过使用字典来获取我需要的数据作为初始数据来预填充表单。除了 Django 引用的 FileFields 或 FieldFile 之外,一切都按预期工作。我已经阅读了一些关于 SO 的类似文章......但没有任何帮助。我了解安全问题,我并不想“强制”上传。我只是想获取附件名称并将其复制到另一个模型。我的表单已提交,但附件未被处理。
这是我的代码....
HTML...
<form method="POST" enctype="multipart/form-data" id="forms">
{{ procedure_attachment_form.management_form }}
{{ procedure_attachment_form.non_form_errors }}
{% for fileform in procedure_attachment_form.forms %}
{{ fileform.id }}
<div class="inline {{ procedure_attachment_form.prefix }}">
{{ fileform.attachments }}
{% if procedure_attachment_form.non_form_errors %}
<h3 class="spacer174">
{{ procedure_attachment_form.non_form_errors }}
</h3>
{% endif %}
{% if fileform.attachments.errors %}
<h3 class="spacer174">
{{ fileform.attachments.errors }}
</h3>
{% endif %}
{{ fileform.procedure.as_hidden }}
</div>
{% endfor %}
我的表格...
class UpdateProcedureFilesForm(forms.ModelForm):
class Meta:
model = UpdateProcedureFiles
fields = ['attachments']
widgets = {
'attachments': ClearableFileInput(attrs={'multiple': True}),
}
我的视图(创建视图)
class UpdateProcedureView(LoginRequiredMixin,CreateView):
model = UpdateProcedure
form_class = UpdateProcedureForm
template_name = 'update_procedure.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
form_class = self.get_form_class()
form = self.get_form(form_class)
dropdown = self.kwargs["pk"]
attachments = ProcedureFiles.objects.filter(procedure_id=dropdown)
attachment_listofdicts = []
for attachment in attachments:
attachment_dict = model_to_dict(attachment)
del attachment_dict['id']
del attachment_dict['procedure']
del attachment_dict['archive_procedure']
del attachment_dict['new_procedure']
del attachment_dict['update_procedure']
print(attachment_dict)
attachment_listofdicts.append(attachment_dict)
UpdateProcedureFileFormSet = inlineformset_factory(UpdateProcedure,
UpdateProcedureFiles,
form=UpdateProcedureFilesForm,
extra=len(attachment_listofdicts),
can_order=True,
min_num=0,
validate_min=True)
procedure_attachment_form = UpdateProcedureFileFormSet(initial=attachment_listofdicts)
# print(procedure_attachment_form)
return self.render_to_response(
self.get_context_data(
form=form,
procedure_attachment_form=procedure_attachment_form,
)
)
def get_object(self, queryset=None):
return get_object_or_404(Procedure, id=self.kwargs['pk'])
def get_initial(self):
initial = super(UpdateProcedureView, self).get_initial()
procedure = Procedure.objects.get(pk=self.kwargs["pk"])
initial = procedure.__dict__.copy()
department = self.request.user.userprofile.department_access.all()
initial.update({
"name": procedure.name,
})
if procedure.department in self.request.user.userprofile.department_access.all() and procedure.access_level == "Default" :
return initial
else:
raise Http404
def get_context_data(self, **kwargs):
context = super(UpdateProcedureView, self).get_context_data(**kwargs)
pk=self.kwargs["pk"]
if self.request.POST:
context["attachments"] = UpdateProcedureFileFormSet(self.request.POST,self.request.FILES)
else:
context["attachments"] = UpdateProcedureFileFormSet()
return context
def form_valid(self, form, procedure_attachment_form):
self.object = form.save()
procedure_attachment_form.instance = self.object
instance = form.save()
return super(UpdateProcedureView, self).form_valid(form)
def form_invalid(self, form, procedure_attachment_form):
return self.render_to_response(
self.get_context_data(form=form,
procedure_attachment_form=procedure_attachment_form,
))
def post(self, request, *args, **kwargs):
print(request.POST)
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Procedures:procedure_main_menu'))
else:
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
user = request.user
userprofile = request.user
procedure_attachment_form = UpdateProcedureFileFormSet(self.request.POST,self.request.FILES)
files = request.FILES.getlist('attachments') #field name in model
if (form.is_valid() and procedure_attachment_form.is_valid()):
procedure_instance = form.save(commit=False)
procedure_instance.user = user
procedure_instance.save()
for f in files:
file_instance = UpdateProcedureFiles(attachments=f, update_procedure=procedure_instance)
file_instance.save()
return self.form_valid(form, procedure_attachment_form)
else:
return self.form_invalid(form, procedure_attachment_form)
同样,一切正常。唯一的例外是涉及 FileFields 时……然后是 nada。提前感谢您的任何想法。
来自 Facebook 群组的一位名叫 Matt Hoskins 的好心先生向我提供了这个解释,经过 3 天的研究,我倾向于相信他。以这样的基本前提结束这一点,即如果可能的话,这并不容易实现。继续采用不同的方法。这是他的更多 eloquent 摘要....啊,我想我明白了这个问题。这根本不是表单集的问题,因为它发生了 - 关键是您正在尝试为未绑定到实例的表单上的文件字段设置初始值。
HTML 文件输入不能采用初始值(这不是 django 的事情,这就是 HTML/browsers 的工作方式),所以当 django 呈现文件字段小部件时,即使底层的小部件也永远不会有初始值数据有一个。
django 如何使用文件字段编辑模型实例是,如果用户在表单上选择一个文件并提交,那么浏览器将提交该文件作为字段的值,django 将更新实例上的字段,但是如果用户不在表单上选择文件,那么浏览器根本不会提交该字段(并不是说它会为该字段提交一个空值,只是 request.FILES 中没有该字段的条目) 并且当发生这种情况时,django 将不会更新实例上的字段(即它将保留其现有值)。
ClearableFileInput 小部件向普通 HTML 文件输入添加了一个额外的 HTML 复选框字段,以允许清除要请求的现有值并将显示任何现有值的名称,但文件输入本身仍然不能在其上存储任何初始值。因此,当用户在没有选择新文件的情况下提交带有 ClearableFileInput 小部件的表单时,该字段的 request.FILES 中没有任何结果(关联的清除复选框字段的值将被提交,但这纯粹是告诉 django 是否清除实例上的文件字段)。
因此,如果您的内联表单集预先填充了实际实例而不是初始实例,那么它会起作用,但是因为您正在尝试根据现有数据创建一组新实例,并且现有数据仅由 HTML 表单,并且因为 html 输入文件字段不能有初始值,所以对于用户没有触摸的字段,你最终什么也没有(事实上你可以看到 ClearableFileInput 小部件选择显示的文件值从初始值开始是误导性的——这些值不是由表单提交的)。
希望这是有道理的......我很快就会写更多的想法
感谢您停下来花时间阅读我的问题。这是我的问题。我正在表单上创建附件。多种的。一切都很好。这就是问题所在……我想在更新表单上“获取”这些附件,以便显示它们,并且如果表单获得批准则可以将其删除。这证明具有挑战性。在某些情况下,我通过使用字典来获取我需要的数据作为初始数据来预填充表单。除了 Django 引用的 FileFields 或 FieldFile 之外,一切都按预期工作。我已经阅读了一些关于 SO 的类似文章......但没有任何帮助。我了解安全问题,我并不想“强制”上传。我只是想获取附件名称并将其复制到另一个模型。我的表单已提交,但附件未被处理。
这是我的代码....
HTML...
<form method="POST" enctype="multipart/form-data" id="forms">
{{ procedure_attachment_form.management_form }}
{{ procedure_attachment_form.non_form_errors }}
{% for fileform in procedure_attachment_form.forms %}
{{ fileform.id }}
<div class="inline {{ procedure_attachment_form.prefix }}">
{{ fileform.attachments }}
{% if procedure_attachment_form.non_form_errors %}
<h3 class="spacer174">
{{ procedure_attachment_form.non_form_errors }}
</h3>
{% endif %}
{% if fileform.attachments.errors %}
<h3 class="spacer174">
{{ fileform.attachments.errors }}
</h3>
{% endif %}
{{ fileform.procedure.as_hidden }}
</div>
{% endfor %}
我的表格...
class UpdateProcedureFilesForm(forms.ModelForm):
class Meta:
model = UpdateProcedureFiles
fields = ['attachments']
widgets = {
'attachments': ClearableFileInput(attrs={'multiple': True}),
}
我的视图(创建视图)
class UpdateProcedureView(LoginRequiredMixin,CreateView):
model = UpdateProcedure
form_class = UpdateProcedureForm
template_name = 'update_procedure.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
form_class = self.get_form_class()
form = self.get_form(form_class)
dropdown = self.kwargs["pk"]
attachments = ProcedureFiles.objects.filter(procedure_id=dropdown)
attachment_listofdicts = []
for attachment in attachments:
attachment_dict = model_to_dict(attachment)
del attachment_dict['id']
del attachment_dict['procedure']
del attachment_dict['archive_procedure']
del attachment_dict['new_procedure']
del attachment_dict['update_procedure']
print(attachment_dict)
attachment_listofdicts.append(attachment_dict)
UpdateProcedureFileFormSet = inlineformset_factory(UpdateProcedure,
UpdateProcedureFiles,
form=UpdateProcedureFilesForm,
extra=len(attachment_listofdicts),
can_order=True,
min_num=0,
validate_min=True)
procedure_attachment_form = UpdateProcedureFileFormSet(initial=attachment_listofdicts)
# print(procedure_attachment_form)
return self.render_to_response(
self.get_context_data(
form=form,
procedure_attachment_form=procedure_attachment_form,
)
)
def get_object(self, queryset=None):
return get_object_or_404(Procedure, id=self.kwargs['pk'])
def get_initial(self):
initial = super(UpdateProcedureView, self).get_initial()
procedure = Procedure.objects.get(pk=self.kwargs["pk"])
initial = procedure.__dict__.copy()
department = self.request.user.userprofile.department_access.all()
initial.update({
"name": procedure.name,
})
if procedure.department in self.request.user.userprofile.department_access.all() and procedure.access_level == "Default" :
return initial
else:
raise Http404
def get_context_data(self, **kwargs):
context = super(UpdateProcedureView, self).get_context_data(**kwargs)
pk=self.kwargs["pk"]
if self.request.POST:
context["attachments"] = UpdateProcedureFileFormSet(self.request.POST,self.request.FILES)
else:
context["attachments"] = UpdateProcedureFileFormSet()
return context
def form_valid(self, form, procedure_attachment_form):
self.object = form.save()
procedure_attachment_form.instance = self.object
instance = form.save()
return super(UpdateProcedureView, self).form_valid(form)
def form_invalid(self, form, procedure_attachment_form):
return self.render_to_response(
self.get_context_data(form=form,
procedure_attachment_form=procedure_attachment_form,
))
def post(self, request, *args, **kwargs):
print(request.POST)
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Procedures:procedure_main_menu'))
else:
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
user = request.user
userprofile = request.user
procedure_attachment_form = UpdateProcedureFileFormSet(self.request.POST,self.request.FILES)
files = request.FILES.getlist('attachments') #field name in model
if (form.is_valid() and procedure_attachment_form.is_valid()):
procedure_instance = form.save(commit=False)
procedure_instance.user = user
procedure_instance.save()
for f in files:
file_instance = UpdateProcedureFiles(attachments=f, update_procedure=procedure_instance)
file_instance.save()
return self.form_valid(form, procedure_attachment_form)
else:
return self.form_invalid(form, procedure_attachment_form)
同样,一切正常。唯一的例外是涉及 FileFields 时……然后是 nada。提前感谢您的任何想法。
来自 Facebook 群组的一位名叫 Matt Hoskins 的好心先生向我提供了这个解释,经过 3 天的研究,我倾向于相信他。以这样的基本前提结束这一点,即如果可能的话,这并不容易实现。继续采用不同的方法。这是他的更多 eloquent 摘要....啊,我想我明白了这个问题。这根本不是表单集的问题,因为它发生了 - 关键是您正在尝试为未绑定到实例的表单上的文件字段设置初始值。 HTML 文件输入不能采用初始值(这不是 django 的事情,这就是 HTML/browsers 的工作方式),所以当 django 呈现文件字段小部件时,即使底层的小部件也永远不会有初始值数据有一个。 django 如何使用文件字段编辑模型实例是,如果用户在表单上选择一个文件并提交,那么浏览器将提交该文件作为字段的值,django 将更新实例上的字段,但是如果用户不在表单上选择文件,那么浏览器根本不会提交该字段(并不是说它会为该字段提交一个空值,只是 request.FILES 中没有该字段的条目) 并且当发生这种情况时,django 将不会更新实例上的字段(即它将保留其现有值)。 ClearableFileInput 小部件向普通 HTML 文件输入添加了一个额外的 HTML 复选框字段,以允许清除要请求的现有值并将显示任何现有值的名称,但文件输入本身仍然不能在其上存储任何初始值。因此,当用户在没有选择新文件的情况下提交带有 ClearableFileInput 小部件的表单时,该字段的 request.FILES 中没有任何结果(关联的清除复选框字段的值将被提交,但这纯粹是告诉 django 是否清除实例上的文件字段)。 因此,如果您的内联表单集预先填充了实际实例而不是初始实例,那么它会起作用,但是因为您正在尝试根据现有数据创建一组新实例,并且现有数据仅由 HTML 表单,并且因为 html 输入文件字段不能有初始值,所以对于用户没有触摸的字段,你最终什么也没有(事实上你可以看到 ClearableFileInput 小部件选择显示的文件值从初始值开始是误导性的——这些值不是由表单提交的)。 希望这是有道理的......我很快就会写更多的想法