How to upload more than 2 files once in Django? - django

I put two files in web, but I can only get one file always.
in web
in form.py
class UFileForm(forms.Form):
file = forms.FileField(label="资料文件上传", widget=forms.ClearableFileInput(attrs={'multiple': True, 'class': 'bg-info'}))
in view.py
class UploadFileView(View):
def post(self, request):
data = {}
form = UFileForm(request.POST, request.FILES)
files = request.FILES.getlist('file')
# print files in request here
print(files)
result is:
[<InMemoryUploadedFile: 20220609奥莉公会-积分统计表.xlsx (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)>]
why there is only one file?

Please see https://stackoverflow.com/a/46409022/14338747 for help using ClearableFileInput functionality with multiple file uploads.
Also, from the Docs. Your views.py must be updated to:
from django.views.generic.edit import FormView
from .forms import UFileForm
class FileFieldFormView(FormView):
form_class = UFileForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file')
if form.is_valid():
for f in files:
... # Do something with each file.
return self.form_valid(form)
else:
return self.form_invalid(form)

Related

Get the real name of saved file (Model) after upload - Django

I'm trying to get the real name of the uploaded file in Django. It turns out that if a file name already exists inside the Model, the Django will create another name aleatory.
For example, if 'abc.xls' is inside the Model database and I try to upload 'abc.xls' again, Django will create a file called 'abc_123456.xls'. That's not the problem!
My question is: how can I get this name ('abc_123456.xls') inside my view.py?
def index(request):
if 'GET' == request.method:
form = DocumentForm()
return render(request, 'auditoria_app/index.html', {'form': form})
else:
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
# I'd like to get the correct file name here!
mediaFolder = settings.MEDIA_ROOT
fileName = f"{mediaFolder}/SAE/{form.cleaned_data['file'].get_alternative_name}" # .xlsm (Excel file)
splitFileName = fileName.split('.')
zipFileName = f"{splitFileName[0]}.zip" # .zip
My model:
from django.db import models
from django.core.validators import FileExtensionValidator
class Document(models.Model):
file = models.FileField(upload_to='SAE/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.file)
You can retrieve it from file with name attribute. Bu it gives you file name with joined MEDIA_ROOT. You can extract MEDIA_ROOT from that attribute. Also you can retrieve object when form.save(commit=False) Try this:
def index(request):
if 'GET' == request.method:
form = DocumentForm()
return render(request, 'auditoria_app/index.html', {'form': form})
else:
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.save()
# you can get file_name like this
file_name = document.file.name.split('/')[-1]

How do I upload multiple images and associate with a model

Errors when attempting to upload multiple images
Description of issue:
I have a model, Vehicle which has vehicle information as well as a cover photo. To add images for a gallery, I have a second model VehicleImage with a foreign key to Vehicle. I'm attempting to upload multiple images with a single file picker and save those all to a defined path (/media/vehicles/vehicle#/) I've tried different views to no avail. Right now i'm getting the following error.
Raised by: main.views.ImageFieldView
No vehicle image found matching the query
Thank you, any help would be greatly appreciated. I'm still learning but I work at it every day.
models.py
def vehicle_directory_path(instance, filename):
#! file will be uploaded to media_cdn/vehicle/<id>/<filename>.<ext>
ext = filename.split('.')[-1]
name = filename.split('.')[0]
vehicle = Vehicle.objects.get(id=instance.id)
return 'vehicles/{0}/{1}.{2}'.format(vehicle.id, name, ext)
class VehicleImage(models.Model):
vehicle = models.ForeignKey('Vehicle', on_delete=models.CASCADE, related_name='images')
image = models.ImageField(upload_to=vehicle_directory_path, null=True, blank=True)
forms.py
class ImageFieldForm(forms.ModelForm):
image_field = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta:
model = VehicleImage
exclude = ('image', 'vehicle')
views.py
class ImageFieldView(CreateView):
model = VehicleImage
form_class = ImageFieldForm
def get(self, request, *args, **kwargs):
context = {'form': ImageFieldForm}
return render(request, 'main/vehicle_upload.html', context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('image_field')
if form.is_valid():
for file in files:
instance = VehicleImage(
vehicle=Vehicle.objects.get(pk=kwargs['pk']),
image=file)
instance.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
urls.py
urlpatterns = [
path('', views.homepage, name='homepage'),
path('vehicles/', views.vehicles, name='vehicles'),
path('vehicles/detail/<int:pk>/', views.VehicleDetail.as_view(), name='vehicle_detail'),
path('vehicles/edit/<int:pk>/', views.VehicleEdit.as_view(), name='vehicle_edit'),
path('vehicles/images/upload/<int:pk>', views.ImageFieldView.as_view(),name='vehicle_image_upload'),
# path('vehicle/image/add/<int:pk>/', views.VehicleEdit.as_view(), name='vehicle_image'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Suggested Edit: Still getting the same error
vehicles = Vehicle.objects.get(pk=kwargs['pk'])
if form.is_valid():
for f in files:
VehicleImage.objects.create(vehicle=vehicles, image=f)
return self.form_valid(form)

How to get multiple files from a django FileField after model form object has been saved

I have a django ModelForm with an additional field to upload some files. However, I need the saved model before I can do anything with the files, and I'm not sure where or how to do that.
I'm following the docs here.
I either need to get the saved model in the FormView or I need to handle it in the Form:
class MessageForm(forms.ModelForm):
class Meta:
model = Message
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
files = self.cleaned_data.get('files')
# do stuff with files here...
# BUT I only get ONE file here, regardless of how many are uploaded with the form.
Is there a way for me to get all the files in the Form's save method?
Or, I can get all the files through the view, but how can I get the saved model that I need? This form is creating a new object, and I need that object before I can do stuff with the files:
class FileFieldView(FormView):
form_class = MessageForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
... # Do something with each file.
# BUT how do I get the saved object? I don't think it has been saved at this point anyway?
return self.form_valid(form)
else:
return self.form_invalid(form)
if form.is_valid():
obj = form.save()
for f in files:
... # Do something with each file.
# BUT how do I get the saved object? I don't think it has been saved at this point anyway?

How to get multiple files from a django FileField after model form object has been saved when form.save() does not return object

I have a django form class that extends django-postman's WriteForm (which is a ModelForm) with an additional field to upload some files.
from postman.forms import WriteForm
class MyWriteForm(WriteForm):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
However, I need the saved model before I can do anything with the files. If I follow the example in the docs, I can extend postman's FormView and overwrite the save() method:
from postman.views import FormView
class MyFormView(FormView):
form_class = MyWriteForm
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
# Do something with each file.
result = form.save() # result is a Boolean instead of the object!
return self.form_valid(form)
else:
return self.form_invalid(form)
However, django-postman WriteForm's save() method does not return the object as expected, and instead returns a Boolean.
Is there any other way I can get the ModelForm's object after it is saved? Either through the view or the form?

Show a paginated ListView and an UpdateView on the same template page

I am trying to create a Django page where something can be updated and something can be viewed in a paginated table. The model looks like this:
class CostGroup(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse(
'costgroup_detail',
kwargs={
'costgroup_pk': self.pk,
}
)
class Cost(models.Model):
cost_group = models.ForeignKey(CostGroup)
amount = models.DecimalField(max_digits=50, decimal_places=2)
def get_absolute_url(self):
return reverse(
'cost_detail',
kwargs={
'cost_pk': self.pk,
}
)
So the edit form is for the name and description fields of the CostGroup model and the table should show a list of the 'amounts`
I previously had it working by just having an UpdateView for the form and the table included in the form template. Now though, as I want to include pagination on the table, I need to use two views on the same page. The page I have designed should look something like this in the end:
I am not worried about the styling at the moment my main focus at the moment is getting the form and the table on the same page. In its current state the only thing that I don't have is the pagination for the table:
The view currently looks like this:
class CostDetail(UpdateView):
model = models.Cost
pk_url_kwarg = 'cost_pk'
template_name = 'main/cost_detail.html'
form_class = forms.CostDetailEditForm
success_url = reverse_lazy('cost_list')
I have a feeling that leveraging the underlying mixins that the Django CBVs use is probably the way to go but I am not sure how to begin with this.
Any help would be much appreciated
Thanks for your time
(This clarification seemed to work better as a new answer)
It looks like you're dealing with both of the tables. The object level is using CostGroup, while the List view is showing the child records from Cost linked to a CostGroup. Assuming that is true, here's how I would proceed:
class CostDetail(ModelFormMixin, ListView):
model = CostGroup # Using the model of the record to be updated
form_class = YourFormName # If this isn't declared, get_form_class() will
# generate a model form
ordering = ['id']
paginate_by = 10
template_name = 'main/cost_detail.html' # Must be declared
def get_queryset(self):
# Set the queryset to use the Cost objects that match the selected CostGroup
self.queryset = Cost.objects.filter(cost_group = get_object())
# Use super to add the ordering needed for pagination
return super(CostDetail,self).get_queryset()
# We want to override get_object to avoid using the redefined get_queryset above
def get_object(self,queryset=None):
queryset = CostGroup.objects.all()
return super(CostDetail,self).get_object(queryset))
# Include the setting of self.object in get()
def get(self, request, *args, **kwargs):
# from BaseUpdateView
self.object = self.get_object()
return super(CostDetail,self).get(request, *args, **kwargs)
# Include the contexts from both
def get_context_data(self, **kwargs):
context = ModelFormMixin.get_context_data(**kwargs)
context = ListView.get_context_data(**context)
return context
# This is the post method found in the Update View
def post(self, request, *args, **kwargs):
# From BaseUpdateView
self.object = self.get_object()
# From ProcessFormView
form = self.get_form()
self.form = form
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def put(self, *args, **kwargs):
return self.post(*args, **kwargs)
I haven't tried to run this, so there may be errors. Good luck!
(Remember ccbv.co.uk is your friend when digging into Class-based Views)
An app I'm working on now uses a similar approach. I start with the ListView, bring in the FormMixin, and then bring in post() from the FormView.
class LinkListView(FormMixin, ListView):
model = Link
ordering = ['-created_on']
paginate_by = 10
template_name = 'links/link_list.html'
form_class = OtherUserInputForm
#=============================================================================#
#
# Handle form input
#
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance with the passed
POST variables and then checked for validity.
"""
form = self.get_form()
self.form = form
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def put(self, *args, **kwargs):
return self.post(*args, **kwargs)
def get_success_url(self):
return reverse('links')
You may also wish to override get_object(), get_queryset(), and get_context().