skip to Main Content

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


  1. Chosen as BEST ANSWER

    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:

    with open(csv_file_path, 'r') as f:
     data = f.read()  #Error here!
     _model.the_file.save(csv_file_path,**data**)
     f.close() 
    

    Instead, I just simply need to do this:

    with open(payroll_file_path, 'rb') as f:
     withdrawal.payroll_success_file.save(payroll_file,**f**)
     f.close()   
    

    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.


  2. 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:

    def create_and_save_the_file():
    from tbkpayments.models import MyModel
    from django.core.files import File
    import os, csv, tempfile, sys
    
    _add_random_line = ['this', 'is', 'a', 'line']
    csv_file_name = 'file.csv'
    
    # Use a context manager to automatically handle file opening and closing
    with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv') as temp_file:
        csv_writer = csv.writer(temp_file)
        # Add lines to the file
        csv_writer.writerow(_add_random_line)
        csv_writer.writerow(_add_random_line)
        csv_writer.writerow(_add_random_line)
        temp_file_path = temp_file.name  # Store the temp file path to use later
    
    # At this point, the temporary file is closed and all data is written
    
    # We create the model to store the file
    _model = MyModel.objects.create()
    
    # Reopen the temporary file in binary mode to read its content
    with open(temp_file_path, 'rb') as f:
        # Use Django's File wrapper to create a file object
        django_file = File(f)
        # Save the file to the model
        _model.the_file.save(csv_file_name, django_file)
    
    # Optionally, delete the temporary file if you no longer need it
    os.remove(temp_file_path)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search