Django - как создать файл и сохранить его в модели FileField?
Вот моя модель. Я хочу создать новый файл и перезаписать существующий при каждом экземпляре экземпляра модели:
class Kitten(models.Model):
claw_size = ...
license_file = models.FileField(blank=True, upload_to='license')
def save(self, *args, **kwargs):
#Generate a new license file overwriting any previous version
#and update file path
self.license_file = ???
super(Request,self).save(*args, **kwargs)
Я вижу много документации о том, как загрузить файл. Но как я могу сгенерировать файл, назначить его в поле модели, а Django сохранить его в нужном месте?
Ответы
Ответ 1
Вы хотите взглянуть на FileField и FieldFile в документации Django, и особенно на FieldFile.save().
По сути, поле, объявленное как FileField
, при обращении к нему дает вам экземпляр класса FieldFile
, который дает вам несколько методов для взаимодействия с базовым файлом. Итак, что вам нужно сделать, это:
self.license_file.save(new_name, new_contents)
где new_name
- это имя файла, которое вы хотите назначить, а new_contents
- содержимое файла. Обратите внимание, что new_contents
должен быть экземпляром либо django.core.files.File
, либо django.core.files.base.ContentFile
(подробности см. в приведенных ссылках на руководство). Два варианта сводятся к следующему:
# Using File
f = open('/path/to/file')
self.license_file.save(new_name, File(f))
# Using ContentFile
self.license_file.save(new_name, ContentFile('A string with the file content'))
Ответ 2
Принятый ответ, безусловно, является хорошим решением, но вот как я начал генерировать CSV и обслуживать его из представления.
#Model
class MonthEnd(models.Model):
report = models.FileField(db_index=True, upload_to='not_used')
import csv
from os.path import join
#build and store the file
def write_csv():
path = join(settings.MEDIA_ROOT, 'files', 'month_end', 'report.csv')
f = open(path, "w+b")
#wipe the existing content
f.truncate()
csv_writer = csv.writer(f)
csv_writer.writerow(('col1'))
for num in range(3):
csv_writer.writerow((num, ))
month_end_file = MonthEnd()
month_end_file.report.name = path
month_end_file.save()
from my_app.models import MonthEnd
#serve it up as a download
def get_report(request):
month_end = MonthEnd.objects.get(file_criteria=criteria)
response = HttpResponse(month_end.report, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=report.csv'
return response
Думал, что стоит поместить это здесь, так как мне потребовалось немного поиграть, чтобы получить все желаемое поведение (перезаписать существующий файл, сохранить в нужное место, не создавать дубликаты файлов и т.д.).
Django 1.4.1
Python 2.7.3
Ответ 3
Спасибо @tawmas. Кроме того,
У меня возникла ошибка, если я не укажу файловый режим при открытии файла. Таким образом,
f = open('/path/to/file', 'r')
Для файла типа ZIP,
f = open('/path/to/file.zip', 'rb')
Ответ 4
Рекомендуется использовать диспетчер контекста или вызывать close()
в случае исключений во время процесса сохранения файла. Это может произойти, если ваш сервер хранения не работает и т.д.
Любое поведение при перезаписи должно быть настроено в вашей системе хранения. Например, S3Boto3Storage имеет настройку AWS_S3_FILE_OVERWRITE
. Если вы используете FileSystemStorage
, вы можете написать пользовательский миксин.
Вы также можете вызвать метод сохранения модели вместо метода сохранения FileField, если вы хотите, чтобы возникали какие-либо пользовательские побочные эффекты, такие как отметки времени последнего обновления. Если это так, вы также можете установить в качестве атрибута имени файла имя файла, что соответствует MEDIA_ROOT
. По умолчанию используется полный путь к файлу, который может вызвать проблемы, если вы его не установите - см. File.__ init __() и File.name.
Вот пример, где self
- это экземпляр модели, где my_file
- это FileField/ImageFile, вызывая save()
для всего экземпляра модели, а не только для FileField:
import os
from django.core.files import File
with open(filepath, 'rb') as fi:
self.my_file = File(fi, name=os.path.basename(fi.name))
self.save()