I need to create a csv report with django python and then save it to a FileField on a model. The filefield stores it to a secure amazon bucket. This is the logic I am using for the model:
class MyModel(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
the_file = models.FileField(blank=True,null=True,upload_to='some/dir/')
def save(self, *args,**kwargs):
super().save(*args,**kwargs)
_access_key = settings.FINANCE_ACCESS
_access_secret = settings.FINANCE_SECRET
_access_bucket = settings.FINANCE_BUCKET
s3 = boto3.client('s3', aws_access_key_id=_access_key, aws_secret_access_key=_access_secret)
try:
if self.the_file:
_the_file_content = default_storage.open(self.the_file.name).read()
s3.put_object(Body=_the_file_content,Bucket=_access_bucket,Key=self.the_file.name)
except Exception as e:
print(type(e),e)
Using the admin site to upload a file, then everything works as expected.
Things start to get tricky when I create the csv file and try to save the model programatically from the view. This is what I am doing:
def create_and_save_the_file():
from tbkpayments.models import MyModel
import os,csv,tempfile,sys
_add_random_line = ['this','is','a','line']
csv_file_name='file.csv'
csv_file_path = os.path.join(tempfile.gettempdir(), csv_file_name)
csv_file = open(csv_file_path, 'w')
csv_writer = csv.writer(csv_file)
# add lines to the file
csv_writer.writerow(_add_random_line)
csv_writer.writerow(_add_random_line)
csv_writer.writerow(_add_random_line)
print(csv_file)
print()
csv_file.close()
# We create the model to store the file
_model=MyModel.objects.create()
with open(csv_file_path, 'r') as f:
data = f.read()
_model.the_file.save(csv_file_path,data)
f.close()
////
And the exception that I am getting is:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "<console>", line 20, in create_and_save_the_file
File "/home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/django/db/models/fields/files.py", line 87, in save
self.name = self.storage.save(name, content, max_length=self.field.max_length)
File "/home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/django/core/files/storage.py", line 51, in save
name = self.get_available_name(name, max_length=max_length)
File "/home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/storages/backends/s3boto3.py", line 620, in get_available_name
return super(S3Boto3Storage, self).get_available_name(name, max_length)
File "/home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/django/core/files/storage.py", line 75, in get_available_name
while self.exists(name) or (max_length and len(name) > max_length):
File "/home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/storages/backends/s3boto3.py", line 516, in exists
name = self._normalize_name(self._clean_name(name))
File "//home/kenny/Projects/backend/virtenvs/venv/lib/python3.8/site-packages/storages/backends/s3boto3.py", line 430, in _normalize_name
raise SuspiciousOperation("Attempted access to '%s' denied." %
django.core.exceptions.SuspiciousOperation: Attempted access to '/tmp/file.csv' denied.
I checked the tmp/ folder permissions (I am under Ubuntu 20.04/lts) and everything looks fine. Plus the file is created and I can access it correctly. What am I missing on the save() method (Note that I am using the filefield.save method)? Seems like a permission problem but I am not quite sure (since uploading the same from admin works…)
2
Answers
Ok, so obviously I was overlooking something (I used and old code of mine where I had to read the file and then send it as a sendgrid attachment): My error is this:
Instead, I just simply need to do this:
Reading the data from f and then trying so store it on a FileField... sometimes is good to have errors like this so you can remember to be humble and get back to the basics from time to time. Hopefully someone find this helpful.
Use Django’s File class when saving a file to a model: This ensures that Django handles the file in a secure and expected manner.
Ensure proper cleanup of temporary files: It’s good practice to ensure that temporary files are cleaned up after their use to avoid unnecessary storage use and potential security issues.
Here’s a revised version of your function that incorporates these changes: