如何使用Inline FormSet通过CreateView上传文件



我无法使用Inline FormSet通过CreateView上传文件。

理想情况下,它将是多个文件类似于管理页面中的行为,但是在这一点上,我正在尝试至少一个。在下面的示例中,一个研讨会应该能够拥有多个文件。

上传时,除了文件

的文件外,所有内容都保存了

models.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

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
)

很可能是罪魁祸首

views.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

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

您不应覆盖post方法 - 它在场景后面调用form_valid,因此它实际上不是处理形式。另一件事是您的FileFormSet没有获得request.FILES-这就是为什么文件形式没有处理它的原因。

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

在我的情况下,将图像通过CreateView上传到S3,而无需表单集。我希望这将帮助寻求这种情况的人。

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文件并声明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

最新更新