仅在 django 中创建基于更新类的视图时,为我的文件上传使用表单集不起作用

Posted

技术标签:

【中文标题】仅在 django 中创建基于更新类的视图时,为我的文件上传使用表单集不起作用【英文标题】:Using formsets for my fileupload does not work when doing an update class based view only on create in django 【发布时间】:2021-03-14 00:34:54 【问题描述】:

我在网上使用了尽可能多的示例,试图让我的两个简单模型能够进行内联表单集,从而允许我将许多文件添加到技术图纸中。 这不起作用,我只能为 Create 添加一个文件,而更新只更新 Technical_Entry 模型,而不是一个文件......这本身就很有趣。创建时的 UI 显示一个添加文件的位置,然后保存整个记录及其子记录。这样可行。 更新后,UI 显示了之前保存的文件..(太棒了!),但随后又有两个“选择文件”插槽(随机?),当单击保存时,向其中添加文件不会执行任何操作。它不会删除之前在创建中添加的文件,但它也 不保存添加到现在三个插槽(一个已使用,两个空闲)的新文件。所以由于某种原因更新不适用于文件。

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

使用表单集上传。虽然页面“显示”大部分正确,但它并没有在 Technical_Entry_Files 模型中创建记录。

相关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'),
        

相关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)

以及 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 %

以及编辑时 UI 的样子...

【问题讨论】:

你在创作的时候能上传多张图片吗? 不,这根本不起作用。因此,专注于进行创建并仅进行一次上传即可工作,确实如此!然后认为第二步是能够在更新视图上编辑该上传以工作,这就是在这里不起作用的。那么接下来...第 3 步是让我上传许多图像,然后最终删除/更改未来的多个图像。 【参考方案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

更新 1.为了在创建时能够添加多个文件,

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. 更新时,修改视图后还是不能更新是因为你没有传递 hidden fields。您没有传递文件的ids,因此,您的表单集没有传递.is_valid(),因此没有更新。添加关于隐藏字段的 for 循环,如下所示。

 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 %

#注意我添加的for loophidden fields

【讨论】:

逐字记录(编辑过的 OP)。还是没有骰子。也发布了编辑时 UI 的屏幕截图... 感谢截图。所以,1)它看起来像实例工作,因为你看到创建的文件。 2) 什么东西不工作? 创造作品!我也可以发布一个屏幕截图。此处发布的是更新视图...它会从数据库中填写应有的字段,如果我更改字段,它确实会尊重该更改并反映在数据库中。它知道它正在尝试编辑文件......有一个“当前”指向我在创建时上传的文件......但是如果我去更改文件,那实际上并没有在数据库中更改或没有磁盘上的新文件。 另外......我不想改变太多,就像删除和添加更多(任意数量)所以文件部分的编辑不起作用,即使它确实有效让我改变......它仍然不是我需要的最终化身。 只是为了确定,您更改了 updateview 的最后一行,对吧?

以上是关于仅在 django 中创建基于更新类的视图时,为我的文件上传使用表单集不起作用的主要内容,如果未能解决你的问题,请参考以下文章

将 html 表单数据获取到基于 django 类的视图中

如何从 django 的表单中获取数据?

仅在单击按钮时运行 celery 任务

如何使用 CBV 在我的所有 Django 模板中创建侧边栏?

用于创建和更新的基于 Django 类的视图

如何通过基于类的视图添加搜索栏