使用表单集将许多文件上传到记录,它不是很有效

Posted

技术标签:

【中文标题】使用表单集将许多文件上传到记录,它不是很有效【英文标题】:Using formsets to upload many files to a record, it isn't quite working 【发布时间】:2021-03-12 22:07:16 【问题描述】:

我在网上使用了尽可能多的示例,试图拼凑出我的两个简单模型:

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)



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

    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)

#        entry = context['object'] 
 #       context['entry_id'] = entry.id
  #      theEntry = Technical_Entry.objects.get(pk=entry.id) 
#        entry_form = Technical_EntryForm(instance=theEntry)
#        context['entry_form'] = entry_form         
        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)

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)

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

它会保存条目,但不会保存上传的文件,我也看不到任何错误。

没有 techdb/files 文件夹(也许我必须创建那个?)但它肯定不会在任何地方失败...创建 Technical_Entry 记录,而不是 Technical_Entry_Files 记录,也没有在磁盘上添加任何东西。

此外,这真的是最大的一块......它只允许我上传一个文件,即使表格应该允许多个文件到一个技术条目? (也许我需要踢一些 java 脚本)。

我发现的所有示例要么不使用基于模型的表单、基于类的视图,要么只是看似矫枉过正。我只需要这些简单的模型就可以让我将许多文件上传到技术条目。我以为我已经很接近了,但我担心我离让它工作还差得很远:/

【问题讨论】:

【参考方案1】:

1.我注意到的一件事是你的&lt;form&gt; 没有

<form action="" method="post" enctype="multipart/form-data">

这里1 说:

没有字符被编码。当您使用具有文件上传控件的表单时,此值是必需的

2. 似乎缺少的另一件事是:request.FILES

data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)

不确定上述内容是否应该纳入您的视图。

以下内容来自文档:about inlineformset 在基于函数的视图中。

if request.method == 'POST':
        formset = ArticleFormSet(request.POST, request.FILES)

文档的另一部分:about File Uploads 说:

当 Django 处理文件上传时,文件数据最终放在 request.FILES 中

【讨论】:

加进去了,还是没有骰子。 谢谢你,额外的一双眼睛帮了大忙。这行得通!所以现在我必须弄清楚如何让我列出已经是条目一部分的文件,并让我添加任意数量的文件......这我真的不知道从哪里开始。但至少我知道上传是在表单集中进行的! 很酷,谢谢! formset = ImageFormSet(request.POST, request.FILES, instance = item) 。这里instance 应该是列出已经是条目一部分的文件。 TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=1, max_num= 5) 您可能需要查看 extramax_num 以“添加任意数量的文件”。需要注意的一件事是,这些会给你更多的形式。但是每个表格(我记得)只能取一个文件。因此,我选择了 js 库来上传图片。 那个JS库可能是要走的路,我正在尝试一种不同的方法,我认为真正不起作用的是我对jquery formset的link-formset调用。我正在使用基于类的视图,所以我认为我的 UpdateView 可能会关心那个 instance=item(我的意思是它可能是已经为该技术条目上传的文件列表......所以仍然在这里查找要做什么。这是我第一次尝试这样的事情,这很可悲。 ***.com/questions/27876644/… 不确定这是否有帮助。

以上是关于使用表单集将许多文件上传到记录,它不是很有效的主要内容,如果未能解决你的问题,请参考以下文章

在 .NET 中编写大型多步骤表单的最有效方法是啥?

使用 Alamofire 上传多部分表单数据文件

Django 内联表单集将始终创建新对象而不是更新它们

返回&文件上传支持 - JSON

使用 ajax 和 laravel 上传文件表单时出现问题?

使用 Alamofire 和 BOX API 的多部分表单上传