简体   繁体   English

如何使用内联表单集通过 CreateView 上传文件?

[英]How do I upload files through a CreateView using an inline formset?

I can't get files to upload through a CreateView using inline formset.我无法使用内联表单集通过 CreateView 上传文件。

Ideally, it would be multiple files similar to how it behaves in the admin page, but at this point, I'm trying to get at least one up.理想情况下,它将是多个文件,类似于它在管理页面中的行为方式,但在这一点上,我正在尝试至少创建一个。 In the following example, one Workshop should be able to have multiple files.在下面的例子中,一个 Workshop 应该可以有多个文件。

When uploading, everything saves except the file of course上传时,当然除了文件之外的所有内容都会保存

models.py模型.py

...


class Workshop (models.Model):
    title = models.CharField(max_length=120)
    created_by = models.ForeignKey(User)
    slug = models.SlugField(blank=True, null=True, unique=True)

    def __str__(self):
        return self.title

...

def upload_workshop_file_loc(instance, filename):
    slug = instance.workshop.slug
    if not slug:
        slug = unique_slug_generator(instance.workshop)
    location = "workshop/{}/".format(slug)
    return location + filename


class WorkshopFile(models.Model):
    workshop = models.ForeignKey(Workshop, related_name='files', on_delete=models.CASCADE)
    name = models.CharField()
    file = models.FileField(
        upload_to=upload_workshop_file_loc,
        null=True,
        validators=[FileExtensionValidator
(allowed_extensions=['pdf', 'ppt'])]
    )

    def __str__(self):
        return str(self.file.name)

...

forms.py表格.py

from django import forms
from .models import Workshop, WorkshopFile
from django.forms.models import inlineformset_factory


class AddWorkshopForm(forms.ModelForm):

    class Meta:
        model = Workshop
        exclude = []


FileFormSet = inlineformset_factory(Workshop,
WorkshopFile,
fields=['workshop','name', 'file'],
exclude=[], 
extra=1, 
can_delete=True
)

Most likely the culprit最有可能的罪魁祸首

views.py视图.py

...
class AddWorkshopView(LoginRequiredMixin, CreateView):

    model = Workshop
    form_class = AddWorkshopForm
    template_name = "modules/add-workshop.html"
    success_url = "/modules/workshop-list/"

    def post(self, request, *args, **kwargs):
        form = AddWorkshopForm(request.POST, request.FILES)
        workshop = form.save(commit=False)
        workshop.save()
        workshop.created_by = request.user
        return redirect('modules:workshop', workshop.slug)


    def get_context_data(self, **kwargs):
        data = super(AddWorkshopView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['files'] = FileFormSet(self.request.POST)
        else:
            data['files'] = FileFormSet()
        return data


    def form_valid(self, form):
        context = self.get_context_data()
        files = context['files']

        with transaction.atomic():
            form.instance.created_by = self.request.user
            form.instance.updated_by = self.request.user
            self.object = form.save()

        if files.is_valid():
            files.instance = self.object
            files.save()

        return super(AddWorkshopView, self).form_valid(form)
...

add-workshop.html添加-workshop.html

...
  <div>
    <form method="post" action='' enctype='multipart/form-data'>
{% csrf_token %}
      {{ form | crispy }}
      <hr/>
        <div>
        {{ files | crispy }}
        </div>

      <input type="submit" class="btn btn-primary btn-md float-left" value="Save" />
    </form>
  </div>
...

You should not override post method - it's calling form_valid behind the scene, so it's not actually handling form.你不应该覆盖post方法 - 它在幕后调用form_valid ,所以它实际上不是在处理表单。 Another thing is that your FileFormSet is not getting request.FILES - that's why form for files is not processing it.另一件事是您的FileFormSet没有收到request.FILES - 这就是文件表单没有处理它的原因。

views.py:视图.py:

class AddWorkshopView(LoginRequiredMixin, CreateView):

    model = Workshop
    form_class = AddWorkshopForm
    template_name = "modules/add-workshop.html"
    success_url = "/modules/workshop-list/"

    def get_context_data(self, **kwargs):
        data = super(AddWorkshopView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['files'] = FileFormSet(self.request.POST, self.request.FILES)
        else:
            data['files'] = FileFormSet()
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        files = context['files']

        with transaction.atomic():
            form.instance.created_by = self.request.user
            form.instance.updated_by = self.request.user
            self.object = form.save()

            if files.is_valid():
                files.instance = self.object
                files.save()

        return super(AddWorkshopView, self).form_valid(form)

In my case, upload the images to s3 via CreateView and without Formset.在我的情况下,通过 CreateView 将图像上传到 s3 而没有 Formset。 I hope this will help someone who looking for this kinda scenario.我希望这会帮助那些寻找这种场景的人。

from bootstrap_modal_forms.generic import (BSModalCreateView)
from vcweb.settings import base
from .forms import InputForm
from utils import upload_to_s3 #global method for upload files to s3
from datetime import datetime
from django.utils.timezone import get_current_timezone

curdatetime = datetime.now(tz=get_current_timezone())
curdatetime=(str(curdatetime)).replace('-','_').replace('+','_').replace('.','_')
curdatetime=curdatetime.replace(':','_').replace(' ','_')


class InputCreateView(BSModalCreateView):
   model = Input_Product
   form_class = InputForm
   template_name = 'inputs/create_product.html'
   success_message = 'Success: Input Product is created.'
   success_url = reverse_lazy('input_product_list')

   def form_valid(self, form):
      form.instance.created_by = self.request.user
      form.instance.updated_by = self.request.user

       # get the image by using request.FILES.get
      filepath = self.request.FILES.get('product_Image', False)
      user_id = self.request.user.id

      #generate filename along with datetime string
      key = base.INPUT_PRODUCT_IMAGE_FOLDER+'/images/' + str(user_id) + '/productimages/' + curdatetime + '.png'

      if filepath == False:
          print(filepath)
      else: #if file exists send the those file and s3 details to upload_to_s3 to upload the data into s3               
          s3status = upload_to_s3(base.S3_KEY, base.S3_SECRET, filepath, base.S3_BUCKET, key, callback=None, md5=None,
                                reduced_redundancy=False, content_type=None)

       # after upload the image, set the filepath value to column(file or image field variable)
       form.instance.product_Image=key
       return super().form_valid(form)

Create utils.py file and declare upload_to_s3 method创建 utils.py 文件并声明 upload_to_s3 方法

import os
import boot
from boto.s3.key import Key

def upload_to_s3(aws_access_key_id, aws_secret_access_key, file, bucket, key, 
          callback=None, md5=None,
             reduced_redundancy=False, content_type=None):
    try:
       size = os.fstat(file.fileno()).st_size
    except:
       # Not all file objects implement fileno(),
       # so we fall back on this
       file.seek(0, os.SEEK_END)
       size = file.tell()

    conn = boto.connect_s3(aws_access_key_id, aws_secret_access_key)
    bucket = conn.get_bucket(bucket, validate=True)
    k = Key(bucket)
    k.key = key
    if content_type:
       k.set_metadata('Content-Type', content_type)
    sent = k.set_contents_from_file(file, cb=callback, md5=md5, 
        reduced_redundancy=reduced_redundancy, rewind=True)

    # Rewind for later use
    file.seek(0)

    if sent == size:
       return True
    return False

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM