[英]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上传时,当然除了文件之外的所有内容都会保存
...
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)
...
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最有可能的罪魁祸首
...
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)
...
...
<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.