簡體   English   中英

如何使用內聯表單集通過 CreateView 上傳文件?

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

我無法使用內聯表單集通過 CreateView 上傳文件。

理想情況下,它將是多個文件,類似於它在管理頁面中的行為方式,但在這一點上,我正在嘗試至少創建一個。 在下面的例子中,一個 Workshop 應該可以有多個文件。

上傳時,當然除了文件之外的所有內容都會保存

模型.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)

...

表格.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
)

最有可能的罪魁禍首

視圖.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)
...

添加-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 - 這就是文件表單沒有處理它的原因。

視圖.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 而沒有 Formset。 我希望這會幫助那些尋找這種場景的人。

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)

創建 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