Django.core.exceptions.SuspiciousOperation при сохранении локально созданного csv-файла в ведро s3

Мне нужно создать отчет в формате csv с помощью django python, а затем сохранить его в FileField в модели. Файловое поле сохраняет его в защищенном ведре amazon. Вот логика, которую я использую для модели:

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)

Использую админку сайта для загрузки файла, после чего все работает как положено.

Когда я создаю csv-файл и пытаюсь программно сохранить модель из представления, начинаются сложности. Вот что я делаю:

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() 

////

А исключение, которое я получаю, следующее:

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.

Я проверил разрешения папки tmp/ (я нахожусь под Ubuntu 20.04/lts), и все выглядит нормально. Плюс файл создан, и я могу получить к нему правильный доступ. Что я упускаю в методе save() (обратите внимание, что я использую метод filefield.save)? Похоже на проблему с правами, но я не совсем уверен (поскольку загрузка того же самого из админки работает...)

Ок, очевидно, я что-то упустил из виду (я использовал старый код, в котором мне нужно было прочитать файл, а затем отправить его как вложение sendgrid): Моя ошибка заключается в следующем:

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

Вместо этого мне просто нужно сделать следующее:

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

Чтение данных из f, а затем попытка сохранить их в FileField... Иногда полезно иметь такие ошибки, чтобы не забывать о скромности и время от времени возвращаться к основам. Надеюсь, кому-то это будет полезно.

При сохранении файла в модель используйте класс File от Django: Это гарантирует, что Django обработает файл безопасным и ожидаемым способом.

Обеспечьте надлежащую очистку временных файлов: Чтобы избежать ненужного хранения и потенциальных проблем с безопасностью, рекомендуется очищать временные файлы после их использования.

Вот пересмотренная версия вашей функции, в которой учтены эти изменения:

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)
Вернуться на верх