在内联表单集中使用 Django FileField
Posted
技术标签:
【中文标题】在内联表单集中使用 Django FileField【英文标题】:Using a Django FileField in an inline formset 【发布时间】:2017-05-26 18:30:34 【问题描述】:在我的应用程序中上传文件时遇到问题。用户提交报告并可以添加附件(通过外键关系)。我已经显示了内联表单,如果将其留空,它将起作用,但是当我尝试上传文件然后提交表单时,我收到 500 错误。基本报告已生成,但内联的附件未保存。
forms.py
class ReportForm(forms.ModelForm):
accidentDate = forms.DateField(widget=SelectDateWidget(
empty_label=("Choose Year",
"Choose Month",
"Choose Day"),
),
label=(u'Accident Date'),
initial=timezone.now())
claimNumber = forms.CharField(max_length=50,
label=(u'Claim Number'),
required=True)
damageEstimate = forms.DecimalField(max_digits=6, decimal_places=2,
label=(u'Damage Estimate'))
description = forms.CharField(widget=forms.Textarea(attrs='rows': 5,
'cols': 80
),
label=(u'Description'),
required=True)
drivable = forms.BooleanField(label=(u'Drivable'),
required=False)
receivedCheck = forms.CharField(max_length=30, label=(u'Who Received Check'))
reported = forms.BooleanField(label=(u'Reported to Insurance Company'),
required=False)
reportedDate = forms.DateField(widget=SelectDateWidget(
empty_label=("Choose Year",
"Choose Month",
"Choose Day"),
),
initial=timezone.now(),
label=(u'Date Reported to Insurance'))
repairsScheduled = forms.DateField(widget=SelectDateWidget(
empty_label=("Choose Year",
"Choose Month",
"Choose Day"),
),
initial=timezone.now(),
label=(u'Scheduled Repair Date'))
repairsCompleted = forms.DateField(widget=SelectDateWidget(
empty_label=("Choose Year",
"Choose Month",
"Choose Day"),
),
initial=timezone.now(),
label=(u'Repair Completion Date'))
repairsPaid = forms.BooleanField(label=(u'Repairs Paid'),
required=False)
subrogationReceived = forms.BooleanField(label=(u'Subrogation Received'),
required=False)
subrogationDate = forms.DateField(widget=SelectDateWidget(
empty_label=("Choose Year",
"Choose Month",
"Choose Day"),
),
initial=timezone.now(),
label=('Subrogation Date'))
class Meta:
model = Report
exclude = ('driver',)
ReportAttachmentFormSet = inlineformset_factory(Report, # parent form
ReportAttachment, # inline-form
fields=['title', 'attachment'], # inline-form fields
# labels for the fields
labels=
'title': (u'Attachment Name'),
'attachment': (u'File'),
,
# help texts for the fields
help_texts=
'title': None,
'attachment': None,
,
# set to false because cant' delete an non-exsitant instance
can_delete=False,
# how many inline-forms are sent to the template by default
extra=1)
相关视图.py CreateView
class ReportCreateView(CreateView):
model = Report
form_class = ReportForm
object = None
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
report_attachment_form = ReportAttachmentFormSet()
return self.render_to_response(
self.get_context_data(form=form,
report_attachment_form=report_attachment_form,
)
)
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
report_attachment_form = ReportAttachmentFormSet(self.request.POST, self.request.FILES)
if form.is_valid() and report_attachment_form.is_valid():
return self.form_valid(form, report_attachment_form)
else:
return self.form_invalid(form, report_attachment_form)
def form_valid(self, form, report_attachment_form):
"""
Called if all forms are valid. Creates Report instance along with the
associated ReportAttachment instances then redirects to success url
Args:
form: Report Form
report_attachment_form: Report attachment Form
Returns: an HttpResponse to success url
"""
self.object = form.save(commit=False)
# pre-processing for Report instance here...
self.object.driver = Profile.objects.get(user=self.request.user)
self.object.save()
# saving ReportAttachment Instances
report_attachments = report_attachment_form.save(commit=False)
for ra in report_attachments:
# change the ReportAttachment instance values here
# ra.some_field = some_value
ra.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, report_attachment_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
Args:
form: Report Form
report_attachment_form: Report Attachment Form
"""
return self.render_to_response(
self.get_context_data(form=form,
report_attachment_form=report_attachment_form
)
)
report_form.html
% block body %
<p>
<form action="" method="post" enctype="multipart/form-data">
% csrf_token %
<fieldset>
form.non_field_errors
% for field in form %
<div class="row">
<div class="col-md-3">% bootstrap_label field.label %</div>
<div class="col-md-8">% bootstrap_field field show_label=False %</div>
</div>
% endfor %
</fieldset>
<fieldset>
<legend>Attachments</legend>
report_attachment_form.management_form
report_attachment_form.non_form_errors
% for form in report_attachment_form %
<div class="inline report_attachment_form.prefix ">
% for field in form.visible_fields %
<div class="row">
<div class="col-md-3">% bootstrap_label field.label %</div>
<div class="col-md-8">% bootstrap_field field show_label=False %</div>
</div>
% endfor %
</div>
% endfor %
</fieldset>
<div class="row">
<div class="col-md-1">
<input type="submit" class="btn btn-groppus-primary bordered" value="Submit" />
</div>
</div>
</form>
</p>
% endblock %
% block scripts %
<script src="% static 'formset/jquery.formset.js' %"></script>
<script type="text/javascript">
$(function()
$(".inline. report_attachment_form.prefix ").formset(
prefix: " report_attachment_form.prefix ", // The form prefix for your django formset
addCssClass: "btn btn-block btn-primary bordered inline-form-add", // CSS class applied to the add link
deleteCssClass: "btn btn-block btn-primary bordered", // CSS class applied to the delete link
addText: 'Add another attachment', // Text for the add link
deleteText: 'Remove attachment above', // Text for the delete link
formCssClass: 'inline-form' // CSS class applied to each form in a formset
)
);
</script>
% endblock %
很难弄清楚这一点,因为它没有给我任何类型的回溯来处理,我在表单中包含了 enc-type,并且我在 views.py 文件中传递了 request.FILES .我的文件上传以正常形式工作,但事实证明内联很麻烦。
如果您需要我澄清任何事情,请告诉我,感谢您的帮助:)
更新:通过使用 @method_decorator(csrf_exempt, name='dispatch')
使 CreateView csrf 豁免获得追溯
这给了我一个 ValueError: save() disabled 以防止由于未保存的相关对象“报告”而导致数据丢失。从跟踪中我可以看到我的文件实际上正在进入内存,问题就在这里:
随着我的进展将继续更新,似乎这种保存 FK 对象的方法在 1.8 之前完美无缺,所以如果我幸运的话,这是一个快速修复对象以正确保存的方法。
【问题讨论】:
【参考方案1】:我需要做的就是将我正在使用的报告表单的实例传递给表单集,既简单又方便。所以在我的 CreateView 的 post 方法中,我将 report_attachment_form 的声明更改为report_attachment_form = ReportAttachmentFormSet(self.request.POST, self.request.FILES, instance=form.instance)
,我们就像黄金一样。
【讨论】:
self.request.FILES 是关键!是的!在任何 CreateView 或 UpdateView ...或文件将不会上传!感谢您的回答以上是关于在内联表单集中使用 Django FileField的主要内容,如果未能解决你的问题,请参考以下文章
如何在内联形式中使用“django-autocomplete-light”