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)