Using formsets for my fileupload does not work when doing an update class based view only on create in django

倖福魔咒の 提交于 2021-01-06 03:51:51

问题


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 ids 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!