问题
I have used as many examples online as I could cobble together in an attempt to get my two simple models to have the ability to do an inline formset allowing me to add many files to a technial drawing. This is not working, I can only add one file for the Create and the update only updates the Technical_Entry model, not a file...which in of itself acts funny. The UI ona create shows one spot to add a file, then save the entire record and its child record. That works. The update, the UI shows the file that was saved earlier..(great!) but then has two more 'choose file' slots (random?) and adding a file to those does nothing when the save is clicked. It doesn't remove the file previously added in the create, but it also does not save the new file added to the now three slots (one used, two free). So update does not work for the files for some reason.
class Technical_Entry(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
ema = models.ForeignKey(EMA, on_delete=models.CASCADE)
system = models.ForeignKey('System', on_delete=models.CASCADE) # are SYSTEMS RELATED TO SUBSYSTEMS OR JUST TWO GROUPS?
sub_system = models.ForeignKey(SubSystem, on_delete=models.CASCADE)
drawing_number = models.CharField(max_length=200)
drawing_title = models.CharField(max_length=255)
engineer = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
date_drawn = models.DateField()
ab = models.BooleanField()
class Technical_Entry_Files(models.Model):
tech_entry = models.ForeignKey(Technical_Entry, on_delete=models.CASCADE)
file = models.FileField(upload_to='techdb/files/')
def __str__(self):
return self.tech_entry.drawing_number
To upload using a formset. While the page 'displays' mostly correctly, it flat out does not create the record in the Technical_Entry_Files model.
Relevant forms.py:
class FileUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FileUploadForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
class Meta:
model = Technical_Entry_Files
fields = ('file',)
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=1, max_num=15)
class Technical_EntryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Technical_EntryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = Technical_Entry
fields = ('category', 'ema', 'system', 'sub_system', 'drawing_number', 'drawing_title', 'engineer', 'vendor', 'date_drawn', 'ab')
widgets = {
'date_drawn':DateInput(attrs={
'class':'datepicker form-control',
'id' : 'datetimepicker2',
'tabindex' : '1',
'placeholder' : 'MM/DD/YYYY hh:mm',
'autocomplete':'off',
}, format='%m/%d/%Y'),
'system' : Select(attrs={'tabindex':'2'}),
}
Relevant views.py:
class TechEntryCreateView(LoginRequiredMixin, CreateView):
print ("we are here")
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
print(template_name)
success_url = '/techentry_add'
def get_context_data(self, **kwargs):
data = super(TechEntryCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)
else:
data['file_upload'] = TechFileFormSet()
return data
def form_valid(self, form):
context =self.get_context_data()
file_upload = context['file_upload']
with transaction.atomic():
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryCreateView, self).form_valid(form)
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryUpdateView, self).form_valid(form)
and the tech_entry_form.html:
{% extends 'base.html' %}
{% load static %}
{% block page-js %}
<script>
$('.link-formset').formset({
addText: 'add file',
deleteText: 'remove',
});
</script>
{% endblock %}
{% block content %}
<main role="main" class="container">
<div class="starter-template">
<h1>New Tech Entry</h1>
</div>
<h2> Details of Technical Entry </h2>
<div class="row">
<div class="col-sm">
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<h2> Files </h2>
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
<input type="submit" value="Save"/><a href="{% url 'tech_database:index' %}">back to the list</a>
</form>
</div>
</div>
</div>
</main><!-- /.container -->
{% endblock %}
And what the UI looks like on edit...
回答1:
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
#log_entry_class = Technical_EntryForm(Technical_Entry) #removed
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
#self.object = self.get_object() #removed
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
#return super().form_valid(form)
return super(TechEntryUpdateView, self).form_valid(form) #replaced old one
UPDATE 1. in order to be able to add mulitple files when creating,
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=4, max_num=4)
# note I changed to extra=4, if you always want to have only 4 files, then also change to max_num=4
2. When update, the reason why the update is not working even after modifying the views was because you were not passing hidden fields
. You were not passing the id
s of the files, therefore, your formsets were not passing .is_valid()
, hence, no update. Add for loop about hidden fields like below.
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
{% for hidden in upload_form.hidden_fields %}
{{hidden}}
{% endfor %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
#Note the for loop
I add about hidden fields
.
来源:https://stackoverflow.com/questions/65101507/using-formsets-for-my-fileupload-does-not-work-when-doing-an-update-class-based