I want to download multiple files in Django without creating zip acrhive.
I have a valid which uses zip (create 1 zip file and download it)
But I have to implement downloading several files without zip archive creating. How can I modify my code?
class DocumentView(GenericAPIView):
def get(self, request, *args, **kwargs):
document_type = kwargs.get("document_type", None)
user_id = kwargs.get("user_id", None)
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
raise NotFound("This is user not found.")
if document_type == 'vehicle_photo':
user_vehicle = user.vehicle.select_related().first()
documents = user_vehicle.photos.all()
else:
documents = user.document_owner.select_related().filter(document_type=document_type)
in_memory = BytesIO()
zip_filename = f"{document_type}_{user_id}.zip"
zip_archive = ZipFile(in_memory, "w")
for document in documents:
f_dir, f_name = os.path.split(document.photo.url if document_type == "vehicle_photo" else
document.file.url)
zip_path = f"{settings.ROOT_DIR}{f_dir}"
zip_archive.write(zip_path+"/"+f_name, f_name)
# Save zip file
zip_archive.close()
response = HttpResponse(content_type="application/zip")
response['Content-Disposition'] = f'attachment; filename={zip_filename}'
in_memory.seek(0)
response.write(in_memory.read())
return response
Related
I'm working on a website where I have multiple excel files uploaded by users and stored in the DB, then a user chooses a file's name and date from a dropdown list and this specific file should be downloaded to him. right now I can get the File object that he requested but can't download it correctly.
This is my Models.py
class File(models.Model):
file_name = models.TextField(max_length=1000, default='myFile')
created_at = models.DateTimeField(
default=datetime.datetime.now().replace(microsecond=0))
file = models.FileField()
file_class = models.CharField(
max_length=20, default='')
def __str__(self):
return self.file_name
This is the part where I store the file in Views.py:
modelFile = File()
now = datetime.datetime.now().replace(microsecond=0)
file_class = request.POST['select_course']
Fname = request.POST['file_name']
myFile = convertToBinaryData(uploaded_fileName)
xlfile = ContentFile(myFile)
modelFile.file.save(Fname, xlfile)
modelFile.file_class = file_class
modelFile.created_at = now
modelFile.file_name = Fname
modelFile.save()
And here is where I retrieve and download the file in Views.py :
def viewTrial(request):
if request.method == "POST":
chosen_filename = request.POST['select_trial']
chosen_date = request.POST['select_trial2']
date = pd.to_datetime(
chosen_date, infer_datetime_format=True).strftime('%Y-%m-%d %H:%M')
FileToView = File.objects.filter(created_at__contains=date).filter(
file_name__contains=chosen_filename)
response = HttpResponse(content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename=%s' % chosen_filename+".xls"
return response
else:
return render(request, "model/trials.html", {})
this code downloads an excel file with only the file name written in it, I've seen multiple questions here regarding this issue and the downloading, and none worked out for me.
if I changed the FileToView to be like this:
FileToView = File.objects.filter(created_at__contains=date).filter(
file_name__contains=chosen_filename).values('file')
then the excel file has a Queryset written in it like this:
{'file': 'myTest'}
where 'myTest' is the file name.
the different thing that I have is that I need to download the file that I've filtered from a list of files, I've been trying for a week now since I'm new to django, and I'll be thrilled to find a solution!
any help is appreciated!
here is my code
def user_invoice_pdf(request,queryset):
template_path = 'user/order_details.html'
context = {'product':queryset}
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="invoice.pdf"'
template = get_template(template_path)
html = template.render(context)
pdf = render_to_pdf(template_path,context)
if pdf:
receipt_file = File(BytesIO(pdf.response))
print(receipt_file,'receipt_file')
invoice = InvoiceData(user_info=request.userinvoie=receipt_file)
invoice.save()
else:
return response
return True
try this
file_name = first_name
file_name2 = file_name+'.'+ "pdf"
user_invoice_obj=Modelsname(user=request.user,order_product=queryset)
user_invoice_obj.save()
user_invoice_obj.invoice.save(file_name2, File(BytesIO(pdf)))
I am trying to build some tests for uploads on my Django site. It allows multiple files to be uploaded so I need to test when multiple files are uploaded.
Testing one file works great:
from django.test import Client
def test_stuff(self):
with open('....\file.csv','rb') as fp:
c = Client()
response = c.post('/', {'name': 'Some Name', 'email': 'some#email.com', 'file': fp})
But trying it with a list of files doesn't work.
def test_stuff(self):
file_list = # get list of file paths to open
myfiles = []
for file in file_list:
with open('....\file.csv','rb') as fp:
myfiles.append(fp)
c = Client()
response = c.post('/', {'name': 'Some Name', 'email': 'some#email.com', 'file':myfiles})
And neither does:
def test_stuff(self):
file_list = # get list of file paths to open
myfiles = []
for file in file_list:
with open('....\file.csv','rb') as fp:
myfiles.append(fp)
c = Client()
response = c.post('/', {'name': 'Some Name', 'email': 'some#email.com',}, files={'file':myfiles})
or
def test_stuff(self):
file_list = # get list of file paths to open
myfiles = []
for file in file_list:
with open('....\file.csv','rb') as fp:
myfiles.append(fp)
c = Client()
response = c.post('/', {'name': 'Some Name', 'email': 'some#email.com'}, files=myfiles)
My view gets the files from request.POST.get('myfiles'), but FILES is empty.
Is there a way to POST multiple files with django test client or should I use something else?
Edited to make more accurate
Part of the problem was that with with, the file is immediately closed exiting the statement. Unsurprisingly, the other part was getting the data in the correct format. Django's test client wants ALL the data as a dictionary so, since i was also sending the username and email, it needed to be formatted like:
def test_stuff(self):
file_list = # get list of file paths to open
data = {}
files = []
for file in file_list:
fp = open('....\file.csv','rb')
files.append(fp)
data['myfiles'] = files
data['name'] = 'Some Name'
data['email'] = 'some#email.com'
c = Client()
response = c.post('/', data)
Also, if you use SimpleUploadedFile you can pass multiple files:
file1 = SimpleUploadedFile('file1.txt', b'file-1')
file2 = SimpleUploadedFile('file2.txt', b'file-2')
response = self.client.post('some url', data={
'file': [ file1, file2]
})
And in the view it could be smth like:
class UploadFormView(FormView):
template_name = '...'
form_class = YourForm
# We require to override the post method to manage multiple files.
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 smth with file
return self.form_valid(form)
else:
return self.form_invalid(form)
I have a small Flask project that gets some inputs from the user and extracts some data from the database based on the input back to the user and returns an output file.
The code works just fine if the file format is csv. However when the file format is xls, I see the output being generated but the flask app does not return the file.
Edited:
Given below is the code for views.py
#app.route('/data', methods=['GET','POST'])
def data():
form = DataForm()
if form.validate_on_submit():
name = form.name.data
start_date = form.start_date_field.data
end_date = form.end_date_field.data
file_extension = form.file_extension_field.data
rep_func(name=name, start_date=start_date, end_date=end_date, exten=file_extension)
current_directory = path.abspath(path.join(__file__, ".."))
base = os.path.join(current_directory, 'files')
if file_extension == 'csv':
data = pd.read_csv(base + f'/final_output/{name}_{end_date}.{file_extension}', sep=r',(?!\s|\Z)', engine='python')
resp = make_response(data.to_csv(index=False))
resp.headers["Content-Disposition"] = f'attachment; filename={name}_{end_date}.{file_extension}'
resp.headers["Content-Type"] = "text/csv"
elif file_extension == 'xls':
data = pd.read_excel(base + f'/final_output/{name}_{end_date}.{file_extension}')
resp = make_response(data.to_excel(index=False))
resp.headers["Content-Disposition"] = f'attachment; filename={name}_{end_date}.{file_extension}'
resp.headers["Content-Type"] = "application/vnd.ms-excel"
return resp
return render_template('file.html', form=form)
Could anyone advise on where am I going wrong with this. Thanks
I have set up a form and view to upload multiple *.gpx files to my website at once. These files are validated using a clean() method on the form and then once validated passed to a function for processing.
When I upload some invalid files the clean() method catches them and informs the user as expected.
When I upload some valid files the processing function crashes with an error saying the files are empty.
If I comment out the clean() method then the valid files are uploaded fine.
What can be happening to the form during the clean() method than means the files are being blanked?
here is my form:
class UploadGpxForm(forms.Form):
gpx_file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
here is my view:
class UploadGpxView(FormView):
form_class = UploadGpxForm
template_name = 'dashboard/upload.html' # Replace with your template.
success_url = reverse_lazy('dashboard:index') # 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('gpx_file')
if form.is_valid():
for f in files:
SaveGPXtoPostGIS(f)
return self.form_valid(form)
else:
return self.form_invalid(form)
Here is my clean method for the UploadGpxForm:
def clean(self):
file_errors=[]
files = list(self.files.getlist('gpx_file'))
for f in list(files):
#check file has only one full stop in it.
if len(f.name.split('.')) != 2:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded:'\
'File type is not supported')
, params = { 'file_name': f.name }
, code = 'file_type')
)
#check file doesn't breach the file size listed in settings
if f.content_type in settings.DASHBOARD_UPLOAD_FILE_TYPES:
if f._size > settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded: File too big.'\
'Please keep filesize under %(setting_size)s.'\
'Current filesize %(file_size)s') ,
params = {
'file_name': f.name,
'setting_size': filesizeformat(
settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE),
'file_size': filesizeformat(f._size)
},
code = 'file_size'
)
)
#check it is one of our allowed file types
else:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded:'\
'File type is not supported')
, params = { 'file_name' : f.name }
, code = 'file_type'
)
)
#next check the file hasn't been uploaded before
#generate MD5
md5hash = md5()
for chunk in f.chunks():
md5hash.update(chunk)
file_hash = md5hash.hexdigest()
if gpxTrack.objects.filter(file_hash=file_hash).exists():
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded as a identical file'\
'has already been uploaded previously'),
params = { 'file_name' : f.name },
code = 'file_hash'))
#finally raise errors if there are any
if len(file_errors) > 0:
raise ValidationError(file_errors)
else:
return files
When you read the file content (for calculating md5 hash) you need to move the file object’s position to the beginning (0th byte) using file.seek:
md5hash = md5()
for chunk in f.chunks():
md5hash.update(chunk)
file_hash = md5hash.hexdigest()
f.seek(0) #<-- add this line